2010-03-05 3 views
6

Ich habe eine Tabelle ItemValue voll von Daten auf einem Server SQL 2005 im Jahr 2000 Kompatibilitätsmodus ausgeführt wird, der so etwas wie (es ist eine benutzerdefinierte Werte Tabelle) aussieht:SQL-Optimierung - Änderung des Ausführungsplans basierend auf dem Constraint-Wert - Warum?

ID ItemCode  FieldID Value 
-- ---------- ------- ------ 
1 abc123    1 D 
2 abc123    2 287.23 
4 xyz789    1 A 
5 xyz789    2 3782.23 
6 xyz789    3 23 
7 mno456    1 W 
9 mno456    3 45 
           ... and so on. 

von fieldID kommt die Itemfield Tabelle:

ID FieldNumber DataFormatID Description ... 
-- ----------- ------------ ----------- 
1    1    1 Weight class 
2    2    4 Cost 
3    3    3 Another made up description 
.    .    x xxx 
.    .    x xxx 
.    .    x xxx 
x    91 (we have 91 user-defined fields) 

Weil ich im Jahr 2000 Modus nicht drehen kann, sind wir eine hässliche Abfrage mit CASEs und GROUP BY die Daten zu erhalten Gebäude stecken schauen, wie es sollte für s ome legacy apps, das ist:

ItemNumber Field1 Field2 Field3 .... Field51 
---------- ------ ------- ------ 
    abc123 D  287.23 NULL 
    xyz789 A  3782.23 23 
    mno456 W  NULL  45 

Sie können sehen, dass wir nur diese Tabelle benötigen, um Werte bis zur 51. UDF anzuzeigen. Hier ist die Abfrage:

SELECT 
    iv.ItemNumber, 
    ,MAX(CASE WHEN f.FieldNumber = 1 THEN iv.[Value] ELSE NULL END) [Field1] 
    ,MAX(CASE WHEN f.FieldNumber = 2 THEN iv.[Value] ELSE NULL END) [Field2] 
    ,MAX(CASE WHEN f.FieldNumber = 3 THEN iv.[Value] ELSE NULL END) [Field3] 
     ... 
    ,MAX(CASE WHEN f.FieldNumber = 51 THEN iv.[Value] ELSE NULL END) [Field51] 
FROM ItemField f 
LEFT JOIN ItemValue iv ON f.ID = iv.FieldID 
WHERE f.FieldNumber <= 51 
GROUP BY iv.ItemNumber 

Wenn die Sachgebietsnr Einschränkung < ist = 51, führen der Plan geht so etwas wie:

SELECT <== Computer Scalar <== Stream Aggregate <== Sort (Cost: 70%) <== Hash Match <== (Clustered Index Seek && Table Scan)

und es ist schnell! Ich kann mehr als 100.000 Datensätze in einer Sekunde abrufen, was unseren Bedürfnissen entspricht.

Allerdings, wenn wir mehr UDFs haben und ich die Einschränkung auf alles über (ja, ich testete sie eins nach dem anderen) oder wenn ich es vollständig entfernen, verliere ich die Sortierung im Ausführungsplan, und es wird durch eine ganze Reihe von Parallelism-Blöcken ersetzt, die Ströme sammeln, neu partitionieren und verteilen, und das Ganze ist langsam (30 Sekunden für gerade einmal 1 Datensatz).

Sachgebietsnr hat einen gruppierten, eindeutigen Index, und einen Teil des zusammengesetzten Primärschlüssels mit dem ID Säule (nicht gruppierten Index) in der Itemfield Tabelle ist. Die ItemValueID und ItemNumber Spalten der Tabelle machen eine PK, und gibt es eine extra nicht gruppierten Index auf der ItemNumber Spalte.

Was ist der Grund dafür? Warum ändert sich durch die Änderung meiner Integritätsregel für einfache Ganzzahlen der gesamte Ausführungsplan?

Und wenn Sie dran sind ... was würden Sie anders machen? Es ist ein SQL-Upgrade für ein paar Monate geplant, aber ich muss dieses Problem vorher beheben.

+0

Was ich anders machen würde, ist nicht diese Tabellenstruktur zu verwenden. Es ist viel besser, eine Struktur mit definierten Feldern für die 99% der Datenbedürfnisse zu verwenden, die im Voraus herausgefunden werden können, als eine EAV-Tabelle zu verwenden. Übrigens verhindert der Kompatibilitätsmodus nicht die Verwendung neuer Funktionen, sondern erlaubt die Verwendung von Funktionen, die nicht mehr erlaubt sind. Wenn Sie jedoch auf einer 2005-Datenbank mit einer 2000-Produktionsdatenbank entwickeln, ist es am besten, die neuen Features zu vermeiden. – HLGEM

Antwort

4

SQL Server ist intelligent genug, um bei der Optimierung der Abfragen CHECK Constraints zu berücksichtigen.

Ihre f.FieldNumber <= 51 ist optimiert und der Optimierer sieht, dass die ganzen zwei Tabellen verbunden werden sollten (was am besten mit einem HASH JOIN getan wird).

Wenn Sie die Einschränkung nicht haben, muss die Engine die Bedingung überprüfen und verwendet höchstwahrscheinlich Index Traversal, um dies zu tun. Dies kann langsamer sein.

Könnten Sie bitte die ganzen Pläne für die Abfragen posten? Führen Sie einfach SET SHOWPLAN_TEXT ON und dann die Abfragen.

Update:

Was ist der Grund? Warum ändert sich durch die Änderung meiner Integritätsregel für einfache Ganzzahlen der gesamte Ausführungsplan?

Wenn Sie mit einer Einschränkung die WHERE Bedingung meinen, ist dies wahrscheinlich die andere Sache.

Set-Operationen (das ist, was SQL tut) haben keinen einzigen effizientesten Algorithmus: Effizienz jedes Algorithmus hängt stark von der Datenverteilung in den Mengen.

Angenommen, Sie nehmen eine Teilmenge (das ist die WHERE-Klausel). Sie können entweder den Datensatzbereich im Index finden und die Indexsatzzeiger verwenden, um die Datenzeilen in der Tabelle zu suchen, oder einfach alle Datensätze durchsuchen die Tabelle und filtern sie unter Verwendung der WHERE Bedingung.

Effizienz der früheren Operation ist m × const, das die letzteren ist n, wo m die Anzahl der Aufzeichnung, die die Bedingung ist, n die Gesamtzahl der Datensätze in der Tabelle ist und const > 1.

Dies bedeutet, dass für größere Werte von m der Fullscan effizienter ist.

SQL Server ist sich dessen bewusst und ändert Ausführungspläne entsprechend den Konstanten, die die Datenverteilung in den Mengenoperationen beeinflussen.

Um dies zu tun, SQL Server verwaltet Statistiken: aggregierte Histogramme der Datenverteilung in jeder indizierten Spalte und verwendet sie zum Erstellen der Abfragepläne.

Das Ändern der Ganzzahl in der WHERE-Bedingung beeinflusst nämlich die Größe und die Datenverteilung der zugrunde liegenden Mengen und macht SQL Server, um die Algorithmen zu überdenken, die am besten mit den Mengen dieser Größe und des Layouts funktionieren.

+0

Ich sehe keinen Hinweis auf eine CHECK-Einschränkung in OP ... –

+0

Ich werde die Pläne veröffentlichen, aber ich muss ein paar Änderungen vornehmen, so dass meine realen Tabellen keine identifizierenden Informationen geben (mein Beispiel ist manipuliert). –

+0

@Remus: Als ich es gelesen habe, habe ich geglaubt, dass * wenn die FieldNumber-Einschränkung '<= 51' ist, es um eine 'CHECK'-Bedingung ging, aber Sie haben wahrscheinlich Recht, die @op haben viel die' WHERE'-Bedingung bedeutet. – Quassnoi

0

es dies mit einem ganzen Bündel von Parallelism Blöcken

Versuche ersetzt wird:

SELECT 
    iv.ItemNumber, 
    ,MAX(CASE WHEN f.FieldNumber = 1 THEN iv.[Value] ELSE NULL END) [Field1] 
    ,MAX(CASE WHEN f.FieldNumber = 2 THEN iv.[Value] ELSE NULL END) [Field2] 
    ,MAX(CASE WHEN f.FieldNumber = 3 THEN iv.[Value] ELSE NULL END) [Field3] 
     ... 
    ,MAX(CASE WHEN f.FieldNumber = 51 THEN iv.[Value] ELSE NULL END) [Field51] 
FROM ItemField f 
LEFT JOIN ItemValue iv ON f.ID = iv.FieldID 
WHERE f.FieldNumber <= 51 
GROUP BY iv.ItemNumber 
OPTION (Maxdop 1) 

Durch die Verwendung von Option (MAXDOP 1), das die parellelism im Ausführungsplan verhindern sollte.

+0

Also das funktioniert tatsächlich, aber Sie können OPTION nicht in einer Ansicht, wie ich es brauche. –

0

Bei 66 treffen Sie einen internen Kostenschätzungsschwellenwert, der entscheidet, dass es besser ist, einen Plan gegen den anderen zu verwenden. Was diese Schwelle ist und warum es passiert, ist nicht wirklich wichtig. Beachten Sie, dass sich Ihre Abfrage bei jedem FieldNumber-Wert unterscheidet, da Sie nicht nur die WHERE ändern, sondern auch die projizierten Pseudo-Pivot-Felder.

Nun weiß ich nicht alle Details Ihrer Tabelle und Ihre Fragen und insert/update/delete/Muster, aber für die jeweilige Abfrage geschrieben Sie die richtige gruppierten Indexstruktur für die ItemValue Tabelle ist dies:

Diese Struktur eliminiert die Notwendigkeit, die Ergebnisse für diese 'Pivot'-Abfrage zwischenzuspeichern.