Wolne zamówienie przez SQL Server

12

W mojej aplikacji mam zapytanie, które wykonuje wyszukiwanie w tabeli „plików”.

Tabela „pliki” jest podzielona na „f”. „Utworzono” (patrz definicja tabeli i ma ~ 26 milionów wierszy dla klienta 19 („f”. „Cid = 19).

Chodzi o to, że jeśli wykonam to zapytanie:

SELECT "f"."id" AS "FileId"  
, "f"."name" AS "FileName"  
, "f"."year" AS "Fileyear"  
, "f"."cid" AS "clientId"
, "f"."created" AS "FileDate"
, CASE WHEN ("vnVE0"."value" is not null AND "vnVE0"."value" != '')                           
                                THEN CAST("vnVE0"."value" AS decimal(28,2))
                                ELSE 0 END AS "keywordValueCol0_numeric"
FROM files "f"  
OUTER APPLY
(
    SELECT DISTINCT
        VT.[value]
    FROM dbo.value_number AS VT
    WHERE
        VT.id_file = F.id
        AND VT.id_field = 260
) AS "vnVE0"
WHERE "grapado" IS NULL AND "masterversion" IS NULL AND ("f"."year" = 2013 OR "f"."year" = 0) AND "f"."cid" = 19
GROUP BY "f"."id", "f"."name", "f"."year", "f"."cid", "f"."created", CASE WHEN ("vnVE0"."value" is not null AND "vnVE0"."value" != '')                           
                            THEN CAST("vnVE0"."value" AS decimal(28,2))
                            ELSE 0 END
ORDER BY (SELECT NULL)
OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY;

Otrzymuję wyniki w 0 sekund, przy następującym planie wykonania: https://www.brentozar.com/pastetheplan/?id=SkV0-FDcG

Zapytanie bez ORDER BY

Ale jeśli spróbuję zamówić według „nazwy”, zapytanie stanie się zbyt wolne:

SELECT "f"."id" AS "FileId"  
, "f"."name" AS "FileName"  
, "f"."year" AS "Fileyear"  
, "f"."cid" AS "clientId"
, "f"."created" AS "FileDate"
, CASE WHEN ("vnVE0"."value" is not null AND "vnVE0"."value" != '')                           
                                THEN CAST("vnVE0"."value" AS decimal(28,2))
                                ELSE 0 END AS "keywordValueCol0_numeric"
FROM files "f"  
OUTER APPLY
(
    SELECT DISTINCT
        VT.[value]
    FROM dbo.value_number AS VT
    WHERE
        VT.id_file = F.id
        AND VT.id_field = 260
) AS "vnVE0"
WHERE "grapado" IS NULL AND "masterversion" IS NULL AND ("f"."year" = 2013 OR "f"."year" = 0) AND "f"."cid" = 19
GROUP BY "f"."id", "f"."name", "f"."year", "f"."cid", "f"."created", CASE WHEN ("vnVE0"."value" is not null AND "vnVE0"."value" != '')                           
                            THEN CAST("vnVE0"."value" AS decimal(28,2))
                            ELSE 0 END
ORDER BY "f"."name"
OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY;

To zapytanie zajmuje 11 minut, aby zwrócić mi wyniki, z następującym planem wykonania: https://www.brentozar.com/pastetheplan/?id=Sk3Fbtv9M

Zapytanie za pomocą ORDER BY

Ponadto, jeśli zmienię kolejność według kolumny, wynik będzie taki sam.

Jak widać w planie wykonania, indeks „files_mv” kosztuje 61%, jest to definicja indeksu:

CREATE NONCLUSTERED INDEX [files_mv] ON [dbo].[files]
(
    [masterversion] ASC,
    [year] ASC,
    [cat_id] ASC,
    [cid] ASC,
    [eid] ASC,
    [grapado] ASC,
    [sub_id] ASC,
    [tip_id] ASC
)
INCLUDE (   [id],
    [name]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO

Używam SQL Server z Azure. W szczególności baza danych SQL Azure z warstwą cen / modeli „S4 Estándar (200 DTU)”.

Otrzymuję dużo danych, ale myślę, że połączenie internetowe nie stanowi wąskiego gardła, ponieważ w innych zapytaniach również otrzymuję dużo danych i są one szybsze.

Ponadto robię ogromne wstawianie danych w tych tabelach, a za kilka dni będę mieć do ~ 240 milionów wierszy w tabeli plików (dla jednego cid) i ~ 480 milionów wierszy w tabeli wartość_numer.

dodatkowe informacje

Funkcja podziału „PF_files_partitioning”:

CREATE PARTITION FUNCTION PF_files_partitioning (DATETIME2(7))
AS
RANGE LEFT FOR VALUES ( '2013-03-31 23:59:59', 
                        '2013-06-30 23:59:59',
                        '2013-09-30 23:59:59',
                        '2013-12-31 23:59:59',
                        '2014-03-31 23:59:59', 
                        '2014-06-30 23:59:59',
                        '2014-09-30 23:59:59',
                        '2014-12-31 23:59:59',
                        '2015-03-31 23:59:59', 
                        '2015-06-30 23:59:59',
                        '2015-09-30 23:59:59',
                        '2015-12-31 23:59:59',
                        '2016-03-31 23:59:59', 
                        '2016-06-30 23:59:59',
                        '2016-09-30 23:59:59',
                        '2016-12-31 23:59:59',
                        '2017-03-31 23:59:59', 
                        '2017-06-30 23:59:59',
                        '2017-09-30 23:59:59',
                        '2017-12-31 23:59:59',
                        '2018-03-31 23:59:59')

Schemat partycji „PS_files_partitioning”:

CREATE PARTITION SCHEME PS_files_partitioning AS PARTITION PF_files_partitioning ALL TO ([PRIMARY]);

** Będę miał około 15 milionów wierszy w każdej partycji.

Tabela plików:

CREATE TABLE [dbo].[files](
    [id] [bigint] IDENTITY(1,1) NOT NULL,
    [cid] [tinyint] NOT NULL,
    [eid] [bigint] NOT NULL,
    [cat_id] [bigint] NOT NULL,
    [tip_id] [bigint] NULL,
    [sub_id] [bigint] NULL,
    [year] [smallint] NOT NULL,
    [caducidad] [smallint] NULL,
    [grapadopri] [int] NOT NULL,
    [grapado] [bigint] NULL,
    [name] [nvarchar](255) NOT NULL,
    [extension] [tinyint] NOT NULL,
    [size] [bigint] NOT NULL,
    [id_doc] [bit] NOT NULL,
    [observaciones] [nvarchar](255) NOT NULL,
    [indexed] [bit] NOT NULL,
    [signed] [bit] NOT NULL,
    [created] [datetime2](7) NOT NULL,
    [name_lower] [nvarchar](255) NOT NULL,
    [modified] [datetime2](7) NULL,
    [related] [bit] NOT NULL,
    [masterversion] [bigint] NULL,
    [versioned] [bit] NOT NULL,
    [hwsignature] [tinyint] NOT NULL,
    [blockedUserId] [smallint] NULL,
 CONSTRAINT [PK_files_id] PRIMARY KEY CLUSTERED 
(
    [id] ASC,
    [created] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created]),
 CONSTRAINT [files$estructure_unique] UNIQUE NONCLUSTERED 
(
    [cat_id] ASC,
    [tip_id] ASC,
    [sub_id] ASC,
    [year] ASC,
    [name] ASC,
    [grapado] ASC,
    [created] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)

tabela numer_wartości:

CREATE TABLE [dbo].[value_number](
    [id] [bigint] IDENTITY(1,1) NOT NULL,
    [id_file] [bigint] NOT NULL DEFAULT ((0)),
    [id_field] [bigint] NOT NULL DEFAULT ((0)),
    [value] [nvarchar](255) NULL DEFAULT (NULL),
    [id_doc] [bigint] NULL DEFAULT (NULL)
 CONSTRAINT [PK_value_number_id] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)

Indeksy tabeli plików

CREATE NONCLUSTERED INDEX [files_clientes] ON [dbo].[files]
(
    [cid] ASC
)
INCLUDE ([id]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [files_grapado] ON [dbo].[files]
(
    [grapado] ASC
)
INCLUDE (   [id],
    [name]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [files_mv] ON [dbo].[files]
(
    [masterversion] ASC,
    [year] ASC,
    [cat_id] ASC,
    [cid] ASC,
    [eid] ASC,
    [grapado] ASC,
    [sub_id] ASC,
    [tip_id] ASC
)
INCLUDE (   [id],
    [name]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [files_ocr] ON [dbo].[files]
(
    [cid] ASC,
    [grapado] ASC,
    [indexed] ASC,
    [masterversion] ASC,
    [extension] ASC
)
INCLUDE (   [id],
    [eid],
    [cat_id],
    [tip_id],
    [sub_id],
    [year],
    [name]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [files_ocr2] ON [dbo].[files]
(
    [cid] ASC,
    [eid] ASC,
    [grapado] ASC,
    [indexed] ASC,
    [masterversion] ASC,
    [extension] ASC
)
INCLUDE (   [id],
    [cat_id],
    [tip_id],
    [sub_id],
    [year],
    [name]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [files_ocr3] ON [dbo].[files]
(
    [cid] ASC,
    [cat_id] ASC,
    [grapado] ASC,
    [indexed] ASC,
    [masterversion] ASC,
    [extension] ASC
)
INCLUDE (   [eid],
    [tip_id],
    [sub_id],
    [year],
    [name]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [busqueda_name] ON [dbo].[files]
(
    [cid] ASC,
    [eid] ASC,
    [grapado] ASC,
    [year] ASC
)
INCLUDE (   [id],
    [cat_id],
    [tip_id],
    [sub_id],
    [grapadopri],
    [name],
    [size],
    [id_doc],
    [signed],
    [created],
    [modified],
    [related],
    [masterversion]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [busqueda2] ON [dbo].[files]
(
    [cid] ASC,
    [eid] ASC,
    [cat_id] ASC,
    [grapado] ASC,
    [masterversion] ASC,
    [year] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [cid] ON [dbo].[files]
(
    [cid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [eid] ON [dbo].[files]
(
    [eid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [extension] ON [dbo].[files]
(
    [extension] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [FK_files_archivo] ON [dbo].[files]
(
    [grapado] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [FK_files_tipo] ON [dbo].[files]
(
    [tip_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [grapadopri] ON [dbo].[files]
(
    [grapadopri] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [index_all] ON [dbo].[files]
(
    [cid] ASC,
    [eid] ASC,
    [grapado] ASC,
    [masterversion] ASC
)
INCLUDE (   [cat_id],
    [tip_id],
    [sub_id],
    [year],
    [grapadopri],
    [name],
    [size],
    [id_doc],
    [signed],
    [created],
    [modified],
    [related],
    [versioned]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [missing_index_7_6] ON [dbo].[files]
(
    [cid] ASC,
    [eid] ASC,
    [grapado] ASC,
    [name] ASC,
    [year] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [ocrCloudClients] ON [dbo].[files]
(
    [grapado] ASC,
    [indexed] ASC,
    [extension] ASC
)
INCLUDE (   [cid],
    [eid],
    [cat_id],
    [tip_id],
    [sub_id]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [searchEntity] ON [dbo].[files]
(
    [cid] ASC,
    [eid] ASC,
    [grapado] ASC,
    [masterversion] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

CREATE NONCLUSTERED INDEX [sub_id] ON [dbo].[files]
(
    [sub_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON PS_files_partitioning([created])

Indeksy tabeli wartość_numer

CREATE NONCLUSTERED INDEX [searchValues] ON [dbo].[value_number]
(
    [id_field] ASC
)
INCLUDE (   [id_file],
    [value]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

CREATE NONCLUSTERED INDEX [search] ON [dbo].[value_number]
(
    [id_file] ASC,
    [id_field] ASC
)
INCLUDE (   [value]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

CREATE NONCLUSTERED INDEX [id_field] ON [dbo].[value_number]
(
    [id_field] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

CREATE NONCLUSTERED INDEX [FK_valueesN_documento] ON [dbo].[value_number]
(
    [id_doc] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

CREATE NONCLUSTERED INDEX [FK_valueesN_archivo] ON [dbo].[value_number]
(
    [id_file] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

Statystyki są aktualne. Zmieniłem typy danych dla roku i innych kolumn, a teraz wydajność wydaje się nieco lepsza, ale plan wykonania jest nadal taki sam. Próbuję naprawić oszacowanie liczności (zmiana indeksu), ale jeszcze nie udało mi się. Zgodnie z dokumentacją platformy Azure powinienem mieć 130 poziom zgodności w bazie danych i mam już 100 z wersją ProductVersion 12.0.

RuSSe
źródło

Odpowiedzi:

9

Analiza

W przypadku zapytania bez preferencji kolejności SQL Server może przesyłać strumieniowo zgrupowane wiersze przy użyciu wyróżnienia przepływu dopasowania mieszania. Jeśli szybko napotka wymaganą liczbę odrębnych wpisów, czas wykonania jest krótki.

Gdy wymagane jest określone zamówienie, SQL Server musi przetestować każdy wiersz. Na przykład, aby nameuporządkować wiersze , należy posortować wszystkie wiersze według nazwy. To będzie powolne, jeśli jest dużo wierszy i nie ma indeksu zapewniającego to zamówienie bez sortowania.

W twoim przypadku istnieje wiele podstawowych komplikacji, w szczególności podział na partycje i rozłączenie [year]. Partycjonowanie oznacza, że ​​Twoje indeksy nie mogą dostarczyć oczekiwanej kolejności. Na przykład indeks włączony namejest faktycznie sortowany najpierw według numeru partycji, a następnie według nazwy. Nie może dostarczyć wierszy posortowanych namepojedynczo.

Ty też FORCED PARAMETERIZATIONustawiłeś. Może to być ogólnie korzystne, ale wiąże się z wpływami, które powinieneś w pełni zrozumieć. To w połączeniu z partycjonowaniem i indeksami wielokolumnowymi oznacza, że ​​twoje statystyki są w dużej mierze bezużyteczne.

Rozbieżność w tym przypadku yearrównież wiąże się z zamawianiem i oznacza, że ​​SQL Server może wyszukiwać tylko year >= 0 and year <= 2013w twoim planie. Jest to znacznie mniej selektywny niż na poszukiwanie year = 0i year = 2013oddzielnie.

Rekomendacje

W świetle powyższego:

Dobrym indeksem dla ORDER BY namezapytania jest:

CREATE INDEX [IX dbo.files cid, year, name : grapado IS NULL AND masterversion IS NULL] 
ON dbo.files (cid, [year], [name])
INCLUDE (grapado, masterversion)
WHERE grapado IS NULL AND masterversion IS NULL;

Lepszym indeksem dla value_numbertabeli jest:

CREATE INDEX [IX dbo.value_number id_file, id_field, value] 
ON dbo.value_number (id_file, id_field, [value]);

Zapytanie można następnie zapisać w celu pobrania maksymalnie 50 wierszy na każdy rok i partycję. Następnie bierzemy pierwsze 50 w kolejności z połączonego zestawu:

WITH PartitionNumbers AS
(
    -- Each partition of the table
    SELECT P.partition_number
    FROM sys.partitions AS P
    WHERE P.[object_id] = OBJECT_ID(N'dbo.files', N'U')
    AND P.index_id = 1
)
SELECT
    FF.id,
    FF.[name],
    FF.[year],
    FF.cid,
    FF.created,
    vnVE0.keywordValueCol0_numeric
FROM PartitionNumbers AS PN
CROSS APPLY
(
    SELECT
        F100.*
    FROM 
    (
        -- 50 rows in order for year 2013
        SELECT
            F.id,
            F.[name],
            F.[year],
            F.cid,
            F.created
        FROM dbo.files AS F
        WHERE
            F.grapado IS NULL
            AND F.masterversion IS NULL
            AND F.[year] = 2013
            AND F.cid = 19
            AND $PARTITION.PF_files_partitioning(F.created) = PN.partition_number
        ORDER BY
            F.[name]
            OFFSET 0 ROWS
            FETCH FIRST 50 ROWS ONLY

        UNION ALL

        -- 50 rows in order for year 0
        SELECT
            F.id,
            F.[name],
            F.[year],
            F.cid,
            F.created
        FROM dbo.files AS F
        WHERE
            F.grapado IS NULL
            AND F.masterversion IS NULL
            AND F.[year] = 0
            AND F.cid = 19
            AND $PARTITION.PF_files_partitioning(F.created) = PN.partition_number
        ORDER BY
            F.[name]
            OFFSET 0 ROWS
            FETCH FIRST 50 ROWS ONLY
    ) AS F100
) AS FF
OUTER APPLY
(
    -- Lookup distinct values
    SELECT
        keywordValueCol0_numeric = 
            CASE
                WHEN VN.[value] IS NOT NULL AND VN.[value] <> ''
                THEN CONVERT(decimal(28, 2), VN.[value])
                ELSE CONVERT(decimal(28, 2), 0)
            END
    FROM dbo.value_number AS VN
    WHERE
        VN.id_file = FF.id
        AND VN.id_field = 260
    GROUP BY
        VN.[value]
) AS vnVE0
ORDER BY
    FF.[name]
    OFFSET 0 ROWS
    FETCH FIRST 50 ROWS ONLY;

Plan wykonania posortuje maksymalnie 100 wierszy z filestabeli:

szacowany plan

Zasadniczo wymagany byłby inny indeks dla każdego innego zamówienia.

Jeśli chcesz policzyć całkowitą liczbę wyników, użyj:

SELECT COUNT_BIG(*) 
FROM dbo.files AS F
OUTER APPLY
(
    SELECT DISTINCT VN.[value]
    FROM dbo.value_number AS VN
    WHERE
        VN.id_file = F.id
        AND VN.id_field = 260
) AS vnVE0
WHERE
    F.grapado IS NULL
    AND F.masterversion IS NULL
    AND F.[year] IN (0, 2013)
    AND F.cid = 19;

skrzypce db <>

Paul White 9
źródło