Aggregationsvorgänge in der Ansicht ignorieren den Index [geschlossen]

8

Das Szenario

Es war einmal eine Staging-Datenbank in einem kleinen Unternehmen, die an einem ETL-Prozess teilnahm und als Empfangskatalog für die verschiedenen Dateiformate aus einer Reihe von Quellen Dritter fungierte. Das E wurde über DTS-Pakete mit wenigen Kontrollstrukturen für die Prüfung oder Kontrolle abgewickelt, wurde jedoch als "gut genug" eingestuft und war dies in jeder Hinsicht.

Die vom E-Teil bereitgestellten Daten waren für den Verbrauch durch eine einzelne Anwendung bestimmt, die von einer Handvoll junger und fähiger Programmierer entwickelt und verwaltet wurde. Obwohl es ihnen an Erfahrung oder Kenntnissen der damaligen Data Warehousing-Techniken mangelte, legten sie ihre eigenen T- und L-Prozesse aus dem Anwendungscode fest und erstellten sie. Diese jungen Software-Ingenieure haben das erfunden, was Außenstehende als "weniger als ideales Rad" bezeichnen könnten, aber mit "Good Enough" als allgegenwärtigem Service-Level konnten sie einen betrieblichen Rahmen schaffen.

Eine Zeit lang war im eng gekoppelten Bereich alles in Ordnung, und der Staging-Katalog stützte sich auf die Daten eines Dutzend Dritter, die wiederum von der Anwendung gespeist wurden. Als die Anwendung wuchs, wuchs auch ihr Appetit, aber mit den geschickten Entwicklern weißer Ritter, die über das System wachten, wurde dieser Appetit schnell und in vielen Fällen sogar gut angegangen.

Aber das goldene Zeitalter konnte natürlich nicht ewig dauern. Mit dem Wohlstand, der durch die erfolgreiche Bewerbung gewährt wurde, wuchs das Geschäft und wuchs. Während des Wachstums mussten die Staging-Umgebung und -Anwendung mitwachsen. Bei aller Wachsamkeit konnte nur eine Handvoll Heldenentwickler nicht mit der Aufrechterhaltung des jetzt expansiven Systems Schritt halten, und die Verbraucher hatten Anspruch auf ihre Daten. Es ging nicht mehr darum, was sie brauchten oder wollten, aber die Bevölkerung hatte das Gefühl, dass sie es einfach verdient hatten und noch mehr forderten.

Mit kaum mehr als Kassen voller Beute bewaffnet, erreichte das Unternehmen den Markt und stellte Entwickler und Administratoren ein, um das stetig wachsende System zu unterstützen. Söldner aller Ethos strömten in das Unternehmen, aber mit diesem Wachstumsschub standen der verfügbaren fachkundigen Anleitung wenig im Wege. Neue Entwickler und Administratoren hatten Mühe, die Feinheiten der selbst gebrauten Suite zu verstehen, bis die Frustrationen zu einem Krieg führten. Jede Abteilung begann zu versuchen, jedes Problem alleine zu lösen und mehr gegeneinander zu arbeiten als miteinander zu arbeiten. Ein einzelnes Projekt oder eine einzelne Initiative würde auf verschiedene Arten umgesetzt, die sich geringfügig voneinander unterscheiden. Die Belastung war für einige der weißen Ritter zu groß, und als sie fielen, brach das Reich zusammen. Bald war das System in Trümmern,

Trotz der Umwandlung dieser vielversprechenden Bereiche in blutigen Spaghetti-Code blieb das Unternehmen bestehen. Es war schließlich "Gut genug".

Die Herausforderung

Ein paar weitere Regimewechsel und Einstellungen später stelle ich fest, dass ich bei der Firma angestellt bin. Seit den großen Kriegen sind viele Jahre vergangen, aber der angerichtete Schaden ist immer noch sehr sichtbar. Ich habe es geschafft, einige der Schwachstellen im E-Teil des Systems zu beheben und einige Steuertabellen hinzuzufügen, während ich die DTS-Pakete auf SSIS aktualisiert habe, die jetzt von einigen tatsächlichen Data Warehousing-Experten verwendet werden, während sie eine normale erstellen und dokumentierter T- und L-Austausch.

Die erste Hürde bestand darin, die Daten aus den Dateien von Drittanbietern so zu importieren, dass die Werte nicht abgeschnitten oder die nativen Datentypen geändert werden, sondern auch einige Steuerschlüssel zum erneuten Laden und Löschen enthalten. Das war alles schön und gut, aber die Anwendungen mussten nahtlos und transparent auf diese neuen Tabellen zugreifen können. Ein DTS-Paket kann eine Tabelle füllen, die dann direkt von der Anwendung gelesen wird. Die SSIS-Upgrades müssen aus QS-Gründen parallel durchgeführt werden. Diese neuen Pakete enthalten jedoch verschiedene Steuerschlüssel und nutzen auch ein Partitionierungsschema. Ganz zu schweigen davon, dass die tatsächlichen Metadatenänderungen allein so bedeutend sein können, dass eine neue Tabelle ohnehin insgesamt gerechtfertigt ist Für die neuen SSIS-Pakete wurde eine neue Tabelle verwendet.

Da zuverlässige Datenimporte jetzt funktionieren und vom Warehousing-Team verwendet werden, besteht die eigentliche Herausforderung darin, die neuen Daten den Anwendungen bereitzustellen, die direkt auf die Staging-Umgebung zugreifen, mit minimalen (auch als "Nein" bezeichneten) Auswirkungen auf den Anwendungscode. Dazu habe ich Gebrauch Ansichten gewählt, eine Tabelle umbenennen, wie dbo.DailyTransactionzu dbo.DailyTranscation_LEGACYund Wiederverwendung des dbo.DailyTransactionObjektnamens für eine Ansicht, die alles wählt effektiv nur von der jetztLEGACYbezeichnete Tabelle. Da das erneute Laden der in diesen Tabellen enthaltenen jahrelangen Daten aus geschäftlicher Sicht keine Option ist, da die neuen SSIS-gefüllten und partitionierten Tabellen ihren Weg in die Produktion finden, werden die alten DTS-Importe deaktiviert und die Anwendungen müssen dies können Greifen Sie auch auf die neuen Daten in den neuen Tabellen zu. Zu diesem Zeitpunkt werden die Ansichten aktualisiert, um die Daten aus den neuen Tabellen ( dbo.DailyTransactionCompletez. B.) auszuwählen, wenn sie verfügbar sind, und aus den Legacy-Tabellen auszuwählen, wenn dies nicht der Fall ist.

In der Tat wird so etwas wie das Folgende getan:

CREATE VIEW dbo.DailyTransaction
AS  SELECT  DailyTransaction_PK, FileDate, Foo
    FROM    dbo.DailyTransactionComplete
    UNION ALL
    SELECT  DailyTransaction_PK, FileDate, Foo
    FROM    dbo.DailyTransaction_LEGACY l
    WHERE NOT EXISTS (  SELECT  1
                        FROM    dbo.DailyTransactionComplete t
                        WHERE   t.FileDate = l.FileDate );

Dies ist zwar logisch einwandfrei, funktioniert jedoch in einer Reihe von Aggregationsfällen überhaupt nicht. Dies führt im Allgemeinen zu einem Ausführungsplan, der einen vollständigen Index-Scan für die Daten in der Legacy-Tabelle durchführt. Dies ist wahrscheinlich in Ordnung für ein paar Dutzend Millionen Datensätze, aber nicht so sehr für ein paar Dutzend Hundert Millionen Datensätze. Da letzteres tatsächlich der Fall ist, musste ich ... "kreativ" sein, was mich dazu führte, eine indizierte Ansicht zu erstellen.

Hier ist der kleine Testfall, den ich eingerichtet habe, einschließlich des FileDateSteuerschlüssels, der auf den Data Warehouse-kompatiblen DateCode_FKPort portiert wurde, um zu veranschaulichen, wie wenig mir die Abfragen gegen die neue Tabelle vorerst wichtig sind:

USE tempdb;
GO

SET NOCOUNT ON;
GO

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'DailyTransaction_LEGACY'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.DailyTransaction_LEGACY;
    CREATE TABLE dbo.DailyTransaction_LEGACY
    (
        DailyTransaction_PK         BIGINT IDENTITY( 1, 1 ) NOT NULL,
        FileDate                    DATETIME NOT NULL,
        Foo                         INT NOT NULL
    );

    INSERT INTO dbo.DailyTransaction_LEGACY ( FileDate, Foo )
    SELECT  DATEADD( DAY, ( 1 - ROW_NUMBER() 
                OVER( ORDER BY so1.object_id ) - 800 ) % 1000, 
                CONVERT( DATE, GETDATE() ) ),
            so1.object_id % 1000 + so2.object_id % 1000
    FROM    sys.all_objects so1
    CROSS JOIN sys.all_objects so2;

    ALTER TABLE dbo.DailyTransaction_LEGACY
    ADD CONSTRAINT PK__DailyTrainsaction
        PRIMARY KEY CLUSTERED ( DailyTransaction_PK )
    WITH ( DATA_COMPRESSION = PAGE, FILLFACTOR = 100 );
END;
GO

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'DailyTransactionComplete'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.DailyTransactionComplete;
    CREATE TABLE dbo.DailyTransactionComplete
    (
        DailyTransaction_PK            BIGINT IDENTITY( 1, 1 ) NOT NULL,
        DateCode_FK                    INTEGER NOT NULL,
        Foo                            INTEGER NOT NULL
    );

    INSERT INTO dbo.DailyTransactionComplete ( DateCode_FK, Foo )
    SELECT  TOP 100000
            CONVERT( INTEGER, CONVERT( VARCHAR( 8 ), DATEADD( DAY, 
                ( 1 - ROW_NUMBER() OVER( ORDER BY so1.object_id ) ) % 100, 
                GETDATE() ), 112 ) ),
            so1.object_id % 1000
    FROM    sys.all_objects so1
    CROSS JOIN sys.all_objects so2;

    ALTER TABLE dbo.DailyTransactionComplete
    ADD CONSTRAINT PK__DailyTransaction
        PRIMARY KEY CLUSTERED ( DateCode_FK, DailyTransaction_PK )
    WITH ( DATA_COMPRESSION = PAGE, FILLFACTOR = 100 );        
END;
GO

In meiner lokalen Sandbox erhalte ich oben eine Legacy-Tabelle mit etwa 4,4 Millionen Zeilen und eine neue Tabelle mit 0,1 Millionen Zeilen mit einer gewissen Überlappung der DateCode_FK/ FileDate-Werte.

Eine MAX( FileDate )gegen die Legacy-Tabelle ohne zusätzliche Indizes läuft ungefähr so, wie ich es erwarten würde.

SET STATISTICS IO, TIME ON;

DECLARE @ConsumeOutput        DATETIME;
SELECT  @ConsumeOutput = MAX( FileDate )
FROM    dbo.DailyTransaction_LEGACY;

SET STATISTICS IO, TIME OFF;
GO

Tabelle 'DailyTransaction_LEGACY'. Scananzahl 1, logische Lesevorgänge 9228, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0.

SQL Server-Ausführungszeiten: CPU-Zeit = 889 ms, verstrichene Zeit = 886 ms.

Clustered Index, Legacy

Wenn Sie einen einfachen Index auf den Tisch werfen, werden die Dinge viel besser. Immer noch ein Scan, aber ein Scan eines Datensatzes anstelle der 4,4 Millionen Datensätze. Ich bin cool damit.

CREATE NONCLUSTERED INDEX IX__DailyTransaction__FileDate
    ON    dbo.DailyTransaction_LEGACY ( FileDate );

SET STATISTICS IO, TIME ON;

DECLARE @ConsumeOutput        DATETIME;
SELECT  @ConsumeOutput = MAX( FileDate )
FROM    dbo.DailyTransaction_LEGACY;

SET STATISTICS IO, TIME OFF;
GO

SQL Server-Analyse- und Kompilierungszeit: CPU-Zeit = 0 ms, verstrichene Zeit = 1 ms. Tabelle 'DailyTransaction_LEGACY'. Scananzahl 1, logische Lesevorgänge 3, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0.

SQL Server-Ausführungszeiten: CPU-Zeit = 0 ms, verstrichene Zeit = 0 ms.

Nicht gruppierter Index, Legacy

Und jetzt erstellen Sie die Ansicht, damit die Entwickler keinen Code ändern müssen, denn das wäre anscheinend das Ende der Welt, wie wir sie kennen. Eine Art Katastrophe.

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'DailyTransaction'
                    AND type = 'V' )
BEGIN
    EXEC( 'CREATE VIEW dbo.DailyTransaction AS SELECT x = 1;' );
END;
GO

ALTER VIEW dbo.DailyTransaction
AS  SELECT  DailyTransaction_PK, FileDate = CONVERT( 
                DATETIME, CONVERT( VARCHAR( 8 ), DateCode_FK ), 112 ), Foo
    FROM    dbo.DailyTransactionComplete
    UNION ALL    
    SELECT  DailyTransaction_PK, FileDate, Foo
    FROM    dbo.DailyTransaction_LEGACY l
    WHERE   NOT EXISTS (    SELECT  1
                            FROM    dbo.DailyTransactionComplete t
                            WHERE   CONVERT( DATETIME, CONVERT( VARCHAR( 8 ),
                                        t.DateCode_FK ), 112 ) = l.FileDate );
GO

Ja, die Unterabfrage ist miserabel, aber dies ist nicht das Problem, und ich werde wahrscheinlich einfach eine persistierte berechnete Spalte erstellen und zu diesem Zweck einen Index darauf werfen, wenn das eigentliche Problem gelöst ist. Also ohne weiteres,

Das Problem

SET STATISTICS IO, TIME ON;

DECLARE @ConsumeOutput1        DATETIME;
SELECT  @ConsumeOutput1 = MAX( FileDate )
FROM    dbo.DailyTransaction;

SET STATISTICS IO, TIME OFF;
GO

SQL Server-Analyse- und Kompilierungszeit: CPU-Zeit = 0 ms, verstrichene Zeit = 4 ms. Tabelle 'DailyTransaction_LEGACY'. Scananzahl 1, logische Lesevorgänge 11972, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0. Tabelle 'Arbeitstabelle'. Scananzahl 0, logische Lesevorgänge 0, physische Lesevorgänge 0, Vorauslesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0. Tabelle 'Arbeitsdatei'. Scananzahl 0, logische Lesevorgänge 0, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0. Tabelle 'DailyTransactionComplete'. Scananzahl 2, logische Lesevorgänge 620, physische Lesevorgänge 0, Vorauslesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0.

SQL Server-Ausführungszeiten: CPU-Zeit = 983 ms, verstrichene Zeit = 983 ms.

Plan anzeigen

Oh, ich verstehe, SQL Server versucht mir zu sagen, dass das, was ich tue, idiotisch ist. Obwohl ich weitgehend zustimme, ändert dies nichts an meiner Lage. Das funktioniert hervorragend für Abfragen , wo die FileDateauf der dbo.DailyTransactionAnsicht in dem Prädikat enthalten ist, aber während des MAXPlan schon schlimm genug ist, der TOPsendet Plan die ganze Sache läuft Süden. Echter Süden.

SET STATISTICS IO, TIME ON;

SELECT  TOP 10 FileDate
FROM    dbo.DailyTransaction
GROUP BY FileDate 
ORDER BY FileDate DESC

SET STATISTICS IO, TIME OFF;
GO

Tabelle 'DailyTransactionComplete'. Scananzahl 2, logische Lesevorgänge 1800110, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0. Tabelle 'DailyTransaction_LEGACY'. Scananzahl 1, logische Lesevorgänge 1254, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0. Tabelle 'Arbeitstabelle'. Scananzahl 0, logische Lesevorgänge 0, physische Lesevorgänge 0, Vorauslesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0. Tabelle 'Arbeitsdatei'. Scananzahl 0, logische Lesevorgänge 0, physische Lesevorgänge 0, Vorauslesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0.

SQL Server-Ausführungszeiten: CPU-Zeit = 109559 ms, verstrichene Zeit = 109664 ms.

oben

Ich erwähnte früher, "kreativ" zu werden, was wahrscheinlich irreführend war. Was ich sagen wollte, war "dümmer". Meine Versuche, diese Ansicht während Aggregationsvorgängen zum Laufen zu bringen, bestanden darin, Ansichten für die Tabellen dbo.DailyTransactionCompleteund zu erstellen dbo.DailyTransaction_LEGACY, die letztere zu binden und zu indizieren und diese Ansicht dann in einer anderen Ansicht mit einem NOEXPANDHinweis zu verwenden auf der Legacy-Ansicht. Während es mehr oder weniger für das funktioniert, was es jetzt tun muss, finde ich die gesamte "Lösung" ziemlich verstörend und gipfelt in Folgendem:

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'v_DailyTransactionComplete'
                    AND type = 'V' )
BEGIN
    EXEC( 'CREATE VIEW dbo.v_DailyTransactionComplete AS SELECT x = 1;' );
END;
GO

ALTER VIEW dbo.v_DailyTransactionComplete
AS  SELECT  DailyTransaction_PK, FileDate = CONVERT( DATETIME, 
                CONVERT( VARCHAR( 8 ), DateCode_FK ), 112 ), 
            Foo
    FROM    dbo.DailyTransactionComplete;
GO

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'v_DailyTransaction_LEGACY'
                    AND type = 'V' )
BEGIN
    EXEC( 'CREATE VIEW dbo.v_DailyTransaction_LEGACY AS SELECT x = 1;' );
END;
GO

ALTER VIEW dbo.v_DailyTransaction_LEGACY
WITH SCHEMABINDING
AS  SELECT  l.DailyTransaction_PK,
            l.FileDate,
            l.Foo,
            CountBig = COUNT_BIG( * )
    FROM    dbo.DailyTransaction_LEGACY l
    INNER JOIN dbo.DailyTransactionComplete n
        ON  l.FileDate <> CONVERT( DATETIME, CONVERT( VARCHAR( 8 ), 
                n.DateCode_FK ), 112 )
    GROUP BY l.DailyTransaction_PK,
            l.FileDate,
            l.Foo;
GO

CREATE UNIQUE CLUSTERED INDEX CI__v_DailyTransaction_LEGACY
    ON dbo.v_DailyTransaction_LEGACY ( FileDate, DailyTransaction_PK )
WITH ( DATA_COMPRESSION = PAGE, FILLFACTOR = 80 );
GO

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'DailyTransaction'
                    AND type = 'V' )
BEGIN
    EXEC( 'CREATE VIEW dbo.DailyTransaction AS SELECT x = 1;' );
END;
GO

ALTER VIEW dbo.DailyTransaction
AS  SELECT  DailyTransaction_PK, FileDate, Foo
    FROM    dbo.v_DailyTransactionComplete
    UNION ALL    
    SELECT  DailyTransaction_PK, FileDate, Foo
    FROM    dbo.v_DailyTransaction_LEGACY WITH ( NOEXPAND );
GO

Wenn der Optimierer gezwungen wird, den von der indizierten Ansicht bereitgestellten Index zu verwenden, verschwinden die MAXund TOPProbleme, aber es muss einen besseren Weg geben, um das zu erreichen, was ich hier versuche. Absolut alle Vorschläge / Schelte wäre sehr dankbar !!

SET STATISTICS IO, TIME ON;

DECLARE @ConsumeOutput1        DATETIME;
SELECT  @ConsumeOutput1 = MAX( FileDate )
FROM    dbo.DailyTransaction;

SET STATISTICS IO, TIME OFF;
GO

Tabelle 'v_DailyTransaction_LEGACY'. Scananzahl 1, logische Lesevorgänge 3, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0. Tabelle 'DailyTransactionComplete'. Scananzahl 1, logische Lesevorgänge 310, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0.

SQL Server-Ausführungszeiten: CPU-Zeit = 31 ms, verstrichene Zeit = 36 ms.

SET STATISTICS IO, TIME ON;

DECLARE @ConsumeOutput1        DATETIME;
SELECT  TOP 10 @ConsumeOutput1 = FileDate
FROM    dbo.DailyTransaction
GROUP BY FileDate 
ORDER BY FileDate DESC

SET STATISTICS IO, TIME OFF;
GO

Tabelle 'v_DailyTransaction_LEGACY'. Scananzahl 1, logische Lesevorgänge 101, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0. Tabelle 'Arbeitstabelle'. Scananzahl 0, logische Lesevorgänge 0, physische Lesevorgänge 0, Vorauslesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0. Tabelle 'Arbeitsdatei'. Scananzahl 0, logische Lesevorgänge 0, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0. Tabelle 'DailyTransactionComplete'. Scananzahl 1, logische Lesevorgänge 310, physische Lesevorgänge 0, Vorlesevorgänge 0, Lob-Lesevorgänge 0, Lob-Lesevorgänge 0, Lobvorlesevorgänge 0.

SQL Server-Ausführungszeiten: CPU-Zeit = 63 ms, verstrichene Zeit = 66 ms.

TL; DR:

Helfen Sie mir zu verstehen, was ich tun muss, um Aggregationsabfragen in der ersten Ansicht, die ich erwähnt habe, in angemessener Zeit mit angemessener E / A-Ressourcennutzung auszuführen.

Avarkx
quelle
3
In einer nicht indizierten Ansicht werden keine Daten gespeichert, und Sie können eine Ansicht nicht mit Unterabfragen, Vereinigungen usw. indizieren. Ich denke, Sie müssen in Betracht ziehen, die Daten auf andere Weise zu materialisieren, z. B. indem Sie diese Ansicht in zwei Ansichten aufteilen und dann gegen sie abfragen oder die Ansicht insgesamt umgehen.
Aaron Bertrand
Ich denke, ich versuche zu erreichen, was Sie vorschlagen, aber ich verstehe nicht ganz, was ich tun muss. Ich denke, dass die Legacy-Ansicht, die ich als Pflaster indiziert und verwirklicht habe, eine ziemlich grobe Problemumgehung für die Einschränkung von Unterabfragen darstellt, und obwohl sie im aktuellen Zustand mehr oder weniger funktioniert, ist sie sehr anfällig für zusätzliche Scope Creep. Ich ringe mit der Idee, einen Prozess zum Auffüllen einer völlig neuen Basistabelle nach einem Import einzurichten und die Ansicht zu ändern, um darauf zu verweisen.
Avarkx

Antworten:

4

Umschreiben NOT EXISTSwie DISTINCTüber eine Ungleichheit join erlaubt die Ansicht indiziert werden, aber es gibt gute Gründe , dies nicht häufig durchgeführt wird.

Der Ausführungsplan, der zum Erstellen des Index für die Ansicht erstellt wurde, ist unvermeidlich schrecklich. Die Ungleichung erzwingt eine physische Verknüpfung mit verschachtelten Schleifen, die mit Ausnahme eines Werts eine Kreuzverknüpfung ist. Das Reduzieren des Produkts mit einer eindeutigen oder äquivalenten Gruppe von führt zu den richtigen Ergebnissen, vorausgesetzt, die Verknüpfungsspalte ist nicht nullwertfähig (wie im Beispielcode), aber niemals effizient. Diese Ineffizienz wird sich mit der Zeit nur verschlimmern und die beteiligten Tabellen werden größer.

Ähnliche Probleme betreffen den Ausführungsplan für alle DML-Anweisungen, die sich auf eine Tabelle auswirken, auf die von der Ansicht verwiesen wird (da die Ansicht in SQL Server jederzeit mit den Basistabellen synchronisiert werden muss). Sehen Sie sich den Ausführungsplan an, der zum Hinzufügen oder Ändern einer einzelnen Zeile in einer der beiden Tabellen erstellt wurde, um zu sehen, was ich meine.

Auf hoher Ebene besteht das Problem, mit dem Sie kämpfen, darin, dass das SQL Server-Abfrageoptimierungsprogramm nicht immer gute Pläne für Ansichten generiert, die a enthalten UNION ALL. Viele der Optimierungen, die wir für selbstverständlich halten (wie MAX-> TOP (1)), werden einfach nicht gewerkschaftsübergreifend umgesetzt.

Für jedes Problem, das Sie lösen, finden Sie einen anderen Fall, in dem keine normale und erwartete Optimierung stattfindet, was zu einem Ausführungsplan mit verzweifelter Leistung führt. Die offensichtliche Lösung besteht darin, die Verwendung von Union in Ansichten zu vermeiden. Wie Sie dies in Ihrem Fall umsetzen, hängt von Details ab, die trotz der Details in der Frage wahrscheinlich nur Ihnen bekannt sind.

Wenn Sie über ausreichend Speicherplatz verfügen, besteht eine Lösung darin, Tabellen separat zu verwalten completeund legacyzu erstellen (einschließlich der nicht vorhandenen Logik). Dies führt zwar zu Datenverdopplungen und ist mit Synchronisierungsproblemen verbunden. Nach meiner Erfahrung sind diese jedoch weitaus einfacher zu lösen als der Versuch, Gewerkschaftsansichten zu erhalten, um unter allen (oder sogar den meisten) Umständen gute Ausführungspläne für eine Vielzahl von Abfragen zu generieren.

Wie Sie sicher wissen, bietet SQL Server eine Reihe von Funktionen zur Unterstützung der Datensynchronisierung, darunter Änderungsverfolgung, Änderungsdatenerfassung, Trigger usw. Die Einzelheiten der Implementierung liegen außerhalb dieses Forums. Der wichtige Punkt ist, dem Optimierer Basistabellen zu präsentieren, nicht alle Ansichten zu vereinen.

Paul White 9
quelle
Vielen Dank an Sie und @AaronBertrand für Ihre Eingabe. Ihre Erkenntnisse und Vorschläge werden sehr geschätzt! Ich werde wahrscheinlich die alten Tabellendaten manuell in die neue Tabelle migrieren, damit ich die Ansicht ändern kann, dass keine Vereinigung mehr von der alten Tabelle erforderlich ist. Theoretisch sollte ich dann in der Lage sein, die Legacy-Tabelle vollständig zu löschen. Es wird andere Herausforderungen mit diesem Ansatz geben, aber wie Sie bereits erwähnt haben, werden diese Herausforderungen auf lange Sicht vielleicht besser zu bewältigen sein, da das, was ich tue, offensichtlich nie gut funktionieren wird.
Avarkx