Keine Partitionseliminierung für Partitionsspaltenwerte in einer Nachschlagetabelle?

7

Ich habe eine partitionierte Tabelle (wie unten gezeigt) erstellt und 480 Millionen Zeilen gesetzt - ungefähr 181 Zeilen pro Konto.

Ich führe Basisabfragen aus, bevor ich Indizes hinzufüge. Ich war überrascht zu sehen, dass das Durchführen von Datumssuchen in der Partitionsspalte auch nach dem Hinzufügen nicht zur Eliminierung der Partition führte option(recompile). Ist das bei partitionierten Tabellen so? Mir scheint, dass dies eher dem wirklichen Leben gleicht als der harten Codierung der Partitionsspaltenwerte des Prädikats.

Schließlich werde ich Index (e) hinzufügen und hier posten, wenn ich Fragen dazu habe. Ich möchte nicht fortfahren, bis ich mit den Antworten in diesem Beitrag zufrieden bin.

    --step 2 (after creating db)
    ALTER DATABASE partitionresearch
    ADD FILEGROUP January
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP February
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP March
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP April
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP May
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP June
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP July
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP August
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP September
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP October
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP November
    GO
    ALTER DATABASE partitionresearch
    ADD FILEGROUP December
    GO
    --step 3
    -- Table Partitioning in SQL Server
        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartJan],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartJan.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [January]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartFeb],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartFeb.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [February]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartMar],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartMar.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [March]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartApr],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartApr.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [April]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartMay],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartMay.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [May]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartJun],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartJun.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [June]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartJul],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartJul.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [July]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartAug],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartAug.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [August]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartSep],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartSep.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [September]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartOct],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartOct.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [October]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartNov],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartNov.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [November]

        ALTER DATABASE [Partitionresearch]
        ADD FILE 
        (
        NAME = [PartDec],
        FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL14.mycompany2\MSSQL\DATA\PartDec.ndf',
            SIZE = 5080 KB, 
            MAXSIZE = UNLIMITED, 
            FILEGROWTH = 2040 KB
        ) TO FILEGROUP [December]

    --step 4
    -- Table Partitioning in SQL Server
    USE Partitionresearch
    GO

    CREATE PARTITION FUNCTION [MonthlyPartition] (date)
    AS RANGE RIGHT FOR VALUES ('20190201', '20190301', '20190401',
                   '20190501', '20190601', '20190701', '20190801', 
                   '20190901', '20191001', '20191101', '20191201');

    --step 5
    -- Table Partitioning in SQL Server
    USE Partitionresearch
    GO

    CREATE PARTITION SCHEME MonthWisePartition
    AS PARTITION MonthlyPartition
            TO (January, February, March, April, May, June, July, 
                August, September, October, November, December
                );
    --step 6
    create table dbo.partitionresearch 
    (
    tranid int identity(1,1),
    [Date] date,

    Account int,
    SeqNumber tinyint,
    AlertType int,
    IsFirst tinyint,
    Indicator1 int,
    [time] time
    )
    on monthwisepartition([date])

    --with partitioning help - 40 seconds (as opposed to 3 min 46 sec) , hovered over table scan and didnt see partition count, but clearly partitions (elimination) were used
    --did see scalar operators with values 5 and 10 which happens to be where these accounts are partition wise (may and october)
    use partitionresearch
    select * from dbo.partitionresearch --hoverd over and closest thing to partn help i saw were scalar operators 5 and 10
    where (date between '5/1/2019' and '5/31/2019' or date between '10/1/2019' and '10/31/2019') and
          account in (1000000,2000000) 
    ------------------------------------------------------------------------------------------------------------------------
    --with "partition help" from a lookup table--3 minutes 33 seconds
    use partitionresearch
    select a.* from dbo.partitionresearch a--hovered over and believe partns wont be used
    join [dbo].[monthlookup] b
    on a.date=b.date
    where account in (1000000,2000000) 
    ------------------------------------------------------------------------------------------------------------------------
--this is the date lookup table which isnt partitioned, thus not aligned
USE [partitionresearch]
GO

/****** Object:  Table [dbo].[monthlookup]    Script Date: 7/12/2019 6:24:35 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[monthlookup](
    [monthid] [int] IDENTITY(1,1) NOT NULL,
    [Date] [date] NOT NULL
) ON [PRIMARY]
GO
user8591443
quelle

Antworten:

7

Dies ist im Produkt für partitionierte Rowstore-Heaps nicht verfügbar. Wenn Sie die Tabelle so ändern, dass sie einen partitionierten Clustered Columnstore-Index enthält, können Sie Partitionen manchmal über die Zeilengruppeneliminierung durch einen Bitmap-Filter entfernen. Dies scheint das zu sein, wonach Sie suchen.

Ich gebloggt dies hier . Einen kleinen Abschnitt zitieren:

Wir wissen, dass SQL Server basierend auf den Daten in der Dimensionstabelle nur zwei Partitionen aus der Faktentabelle lesen muss. Könnte der Abfrageoptimierer theoretisch besser abschneiden als er? Berücksichtigen Sie die Tatsache, dass eine partitionierte Tabelle höchstens 15000 Partitionen enthält. Alle Partitionswerte können sich nicht überlappen und ändern sich nicht ohne eine DDL-Operation. Beim Erstellen der Hash-Tabelle kann das Abfrageoptimierungsprogramm verfolgen, welche Partitionen mindestens eine Zeile enthalten. Am Ende des Hash-Builds wissen wir genau, welche Partitionen Daten enthalten können, sodass der Rest der Partitionen während der Testphase übersprungen werden kann.

Möglicherweise wird dies nicht implementiert, da es wichtig ist, dass der Hash-Build unabhängig von der Sonde ist. Möglicherweise ist zum richtigen Zeitpunkt keine Garantie verfügbar, dass der Bitmap-Operator im Gegensatz zu einem Repartition-Stream-Operator bis zum Scan verschoben wird. Vielleicht ist dies kein häufiger Fall und die Optimierung ist die Mühe nicht wert. Wie oft treten Sie der Partitionierungsspalte bei, anstatt danach zu filtern?

Joe Obbish
quelle
2

Der Vollständigkeit halber können Sie eine dynamische Partitionseliminierung erhalten , jedoch nur, wenn der Join-Typ verschachtelte Schleifen mit korrelierten Parametern sind.

Verwenden Sie beispielsweise die bereitgestellte Partitionierungsfunktion und das bereitgestellte Partitionsschema:

CREATE PARTITION FUNCTION MonthlyPartition ([date])
AS RANGE RIGHT FOR VALUES
(
    '20190201', '20190301', '20190401',
    '20190501', '20190601', '20190701', '20190801', 
    '20190901', '20191001', '20191101', '20191201'
);

CREATE PARTITION SCHEME MonthWisePartition
AS PARTITION MonthlyPartition ALL TO ([PRIMARY]);

und Tabellen:

CREATE TABLE dbo.PartitionResearch 
(
    tranid integer identity(1,1) NOT NULL,
    [Date] date NULL,
    Account integer NULL,
    SeqNumber tinyint NULL,
    AlertType integer NULL,
    IsFirst tinyint NULL,
    Indicator1 integer NULL,
    [time] time NULL
)
ON MonthWisePartition([Date]);

CREATE TABLE dbo.MonthLookup
(
    [MonthId] integer IDENTITY(1,1) NOT NULL,
    [Date] date NOT NULL
)
ON [PRIMARY];

Die Abfrage, bei der die Partitionseliminierung nicht verwendet wurde:

SELECT
    a.* 
FROM dbo.PartitionResearch AS a
JOIN dbo.MonthLookup AS b
    ON a.[Date]=b.[Date]
WHERE
    a.Account IN (1000000,2000000);

... erstellt den folgenden Plan:

Plan für verschachtelte Schleifen

Die Eigenschaften des PartitionResearchTabellenscans zeigen die Eliminierung dynamischer Partitionen unter Verwendung des aktuellen Datums aus der MonthLookupTabelle bei jeder Iteration der Schleife:

Dynamische Partitionseliminierung

Diese Option wird hier bevorzugt, da die Tabellen leer sind. In Ihrem Fall bevorzugte der Optimierer aus geschätzten Kostengründen einen Hash-Join-Plan. Bei einer MonthLookupTabelle mit 56 Zeilen (wie in Ihren Plänen gezeigt) würde die Alternative für verschachtelte Schleifen eine einzelne Partition 56 Mal scannen. Der Optimierer bewertet (wahrscheinlich zu Recht), dass es besser ist, stattdessen alle 12 Partitionen einmal zu scannen.

Wenn Sie Ihre Daten mit dynamischer Partitionseliminierung testen möchten, erhalten Sie möglicherweise einen solchen Plan mit einem OPTION (LOOP JOIN)Abfragehinweis. Mit nur zwei Partitionen, auf die über die Beispielabfrage zugegriffen wird, ist es zumindest plausibel, dass zwei Partitionen in angemessener Zeit jeweils 28 Mal gescannt werden könnten.

Für eine robustere allgemeine Strategie müssten Sie spezifisches T-SQL schreiben, um die Partitionseliminierung zu erreichen, z. B. mithilfe der $PARTITIONFunktion, einer temporären Tabelle oder dynamischem SQL.

Paul White 9
quelle
1

Nachdem ich die von dieser Community vorgegebene Richtung besser verstanden hatte, war ich versucht, einen Prototyp auszuprobieren, bei dem SQL dynamisch basierend auf dem Inhalt der Nachschlagetabelle generiert wird (dh kein Join). Möglicherweise nach dem Ändern der Partitionsspalte in eine neue Spalte, die nur aus Jahr und Monat besteht.

Ich wollte das dynamische SQL nach Bedarf auf 1-12 Variablen mit einer Neukompilierungsoption basieren. Stattdessen (siehe Code unten) und im Interesse der Zeitersparnis habe ich mir einfach selbst bewiesen, dass dynamisches SQL theoretisch genutzt werden kann, um alle (im Rahmen des Zumutbaren) Einschränkungen im Zusammenhang mit Nachschlagetabellen und der Eliminierung von Partitionen zu umgehen.

Ich habe 2 Saiten gebaut. Eine mit 2 Sätzen von Datumsbereichsvariablendeklarationen ( @low1,@high1,@low2,@high2). Eine mit einer Auswahl und einem Prädikat, die auf die Datumsbereichsvariablen und zwei Zielkonten verweisen. Ich habe die Verkettung der beiden Zeichenfolgen ausgeführt und bin überzeugt, dass die Eliminierung von Partitionen verwendet wurde. Die Auswahl hatte am Ende eine Option (neu kompilieren). Der Cache wurde geleert, bevor die Laufzeiten von einem Versuch zum nächsten verglichen wurden ... Äpfel mit Äpfeln. Teilweise um sicherzugehen, dass die Partitionseliminierung tatsächlich stattgefunden hat.

declare @sql1 varchar(500)=
'declare @low1 date ='+'''' +'5/1/19'+'''' +
'declare @low2 date ='+'''' +'10/1/19'+'''' +
'declare @high1 date ='+''''+'5/31/19' +'''' +
'declare @high2 date ='+''''+'10/31/19'+'''' 
declare @sql2 varchar(500)=
'select * from dbo.partitionresearch 
where (date between @low1 and @high1 or date between @low2 and @high2) and
      account in (1000000,2000000) 
OPTION (RECOMPILE)'
CHECKPOINT 
DBCC DROPCLEANBUFFERS
exec (@sql1+@sql2)
user8591443
quelle