Suchen und scannen Sie… auf partitionierten Tabellen

22

Ich habe diese Artikel in PCMag von Itzik Ben-Gan gelesen :

Suchen und Sie scannen Teil I: Wenn das Optimierungsprogramm nicht optimiert,
suchen und Sie scannen Teil II: Aufsteigende Tasten

Ich habe derzeit ein "Grouped Max" -Problem mit all unseren partitionierten Tabellen. Wir verwenden den von Itzik Ben-Gan bereitgestellten Trick , um ein Maximum (ID) zu erhalten, aber manchmal läuft er einfach nicht:

DECLARE @MaxIDPartitionTable BIGINT
SELECT  @MaxIDPartitionTable = ISNULL(MAX(IDPartitionedTable), 0)
FROM    ( SELECT    *
          FROM      ( SELECT    partition_number PartitionNumber
                      FROM      sys.partitions
                      WHERE     object_id = OBJECT_ID('fct.MyTable')
                                AND index_id = 1
                    ) T1
                    CROSS APPLY ( SELECT    ISNULL(MAX(UpdatedID), 0) AS IDPartitionedTable
                                  FROM      fct.MyTable s
                                  WHERE     $PARTITION.PF_MyTable(s.PCTimeStamp) = PartitionNumber
                                            AND UpdatedID <= @IDColumnThresholdValue
                                ) AS o
        ) AS T2;
SELECT @MaxIDPartitionTable 

Ich habe diesen Plan

Bildbeschreibung hier eingeben

Aber nach 45 Minuten sehen Sie sich die Anzeigen an

reads          writes   physical_reads
12,949,127        2       12,992,610

das bekomme ich raus sp_whoisactive.

Normalerweise läuft es ziemlich schnell, aber heute nicht.

Edit: Tabellenstruktur mit Partitionen:

CREATE PARTITION FUNCTION [MonthlySmallDateTime](SmallDateTime) AS RANGE RIGHT FOR VALUES (N'2000-01-01T00:00:00.000', N'2000-02-01T00:00:00.000' /* and many more */)
go
CREATE PARTITION SCHEME PS_FctContractualAvailability AS PARTITION [MonthlySmallDateTime] TO ([Standard], [Standard])
GO
CREATE TABLE fct.MyTable(
    MyTableID BIGINT IDENTITY(1,1),
    [DT1TurbineID] INT NOT NULL,
    [PCTimeStamp] SMALLDATETIME NOT NULL,
    Filler CHAR(100) NOT NULL DEFAULT 'N/A',
    UpdatedID BIGINT NULL,
    UpdatedDate DATETIME NULL
CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED 
(
    [DT1TurbineID] ASC,
    [PCTimeStamp] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION = ROW) ON [PS_FctContractualAvailability]([PCTimeStamp])
) ON [PS_FctContractualAvailability]([PCTimeStamp])

GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_UpdatedID_PCTimeStamp] ON [fct].MyTable
(
    [UpdatedID] ASC,
    [PCTimeStamp] ASC
)
INCLUDE (   [UpdatedDate]) 
WHERE ([UpdatedID] IS NOT NULL)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, DATA_COMPRESSION = ROW) ON [PS_FctContractualAvailability]([PCTimeStamp])
GO
Henrik Staun Poulsen
quelle

Antworten:

28

Das Grundproblem besteht darin, dass auf die Indexsuche kein Top-Operator folgt. Dies ist eine Optimierung, die normalerweise eingeführt wird, wenn die Suche Zeilen in der richtigen Reihenfolge für ein MIN\MAXAggregat zurückgibt .

Diese Optimierung nutzt die Tatsache aus, dass die Min / Max-Zeile die erste in aufsteigender oder absteigender Reihenfolge ist. Es kann auch sein, dass der Optimierer diese Optimierung nicht auf partitionierte Tabellen anwenden kann. Ich vergesse.

Der Punkt ist jedenfalls, dass der Ausführungsplan ohne diese Umwandlung am Ende jede Zeile verarbeitet, die S.UpdatedID <= @IDColumnThresholdValuepro Partition qualifiziert ist , und nicht die gewünschte Zeile pro Partition.

Sie haben in der Frage keine Tabellen-, Index- oder Partitionierungsdefinitionen angegeben, daher kann ich nicht viel genauer sein. Sie sollten überprüfen, ob Ihr Index eine solche Umwandlung unterstützt. Mehr oder weniger in äquivalenter Weise könnte man auch die ausdrücken MAXals ein TOP (1) ... ORDER BY UpdatedID DESC.

Wenn dies zu einer Sortierung (einschließlich einer TopN-Sortierung ) führt, wissen Sie, dass Ihr Index nicht hilfreich ist. Beispielsweise:

SELECT
    @MaxIDPartitionTable = ISNULL(MAX(T2.IDPartitionedTable), 0)
FROM    
( 
    SELECT
        O.IDPartitionedTable
    FROM      
    ( 
        SELECT
            P.partition_number AS PartitionNumber
        FROM sys.partitions AS P
        WHERE 
            P.[object_id] = OBJECT_ID(N'fct.MyTable', N'U')
            AND P.index_id = 1
    ) AS T1
    CROSS APPLY 
    (    
        SELECT TOP (1) 
            S.UpdatedID AS IDPartitionedTable
        FROM fct.MyTable AS S
        WHERE
            $PARTITION.PF_MyTable(S.PCTimeStamp) = T1.PartitionNumber
            AND S.UpdatedID <= @IDColumnThresholdValue
        ORDER BY
            S.UpdatedID DESC
    ) AS O
) AS T2;

Die Planform, die dies erzeugen sollte, ist:

Gewünschte Planform

Beachten Sie den oberen Bereich unterhalb der Indexsuche. Dies begrenzt die Verarbeitung auf eine Zeile pro Partition.

Oder verwenden Sie eine temporäre Tabelle, um Partitionsnummern zu speichern:

CREATE TABLE #Partitions
(
    partition_number integer PRIMARY KEY CLUSTERED
);

INSERT #Partitions
    (partition_number)
SELECT
    P.partition_number AS PartitionNumber
FROM sys.partitions AS P
WHERE 
    P.[object_id] = OBJECT_ID(N'fct.MyTable', N'U')
    AND P.index_id = 1;

SELECT
    @MaxIDPartitionTable = ISNULL(MAX(T2.UpdatedID), 0)
FROM #Partitions AS P
CROSS APPLY 
(
    SELECT TOP (1) 
        S.UpdatedID
    FROM fct.MyTable AS S
    WHERE
        $PARTITION.PF_MyTable(S.PCTimeStamp) = P.partition_number
        AND S.UpdatedID <= @IDColumnThresholdValue
    ORDER BY
        S.UpdatedID DESC
) AS T2;

DROP TABLE #Partitions;

Randnotiz: Der Zugriff auf eine Systemtabelle in Ihrer Abfrage verhindert Parallelität. Wenn dies wichtig ist, ziehen Sie in Betracht, die Partitionsnummern in einer temporären Tabelle zu materialisieren APPLY. Parallelität ist in diesem Muster normalerweise nicht hilfreich (mit korrekter Indizierung), aber es wäre mir ein Dorn im Auge, es nicht zu erwähnen.

Nebenbemerkung 2: Es gibt ein aktives Connect-Element , das eine integrierte Unterstützung für MIN\MAXAggregate und Top auf partitionierten Objekten anfordert .

Paul White sagt GoFundMonica
quelle