Es ist eine einfache Auswahl aus einer temporären Tabelle, bei der eine vorhandene Tabelle auf ihrem Primärschlüssel verknüpft wird, wobei zwei Unterauswahlen unter Verwendung von Top 1 auf die verknüpfte Tabelle verweisen.
In Code:
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
FROM
#TempTable as TempTable
LEFT JOIN
JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn1 AND
TempTable.PKColumn2 = JoinedTable.PKColumn2)
WHERE
JoinedTable.WhereColumn IN (1, 3)
Dies ist eine exakte Nachbildung meiner Anfrage.
Wenn ich die beiden Unterauswahlen entferne, läuft es gut und schnell. Mit den beiden Unterauswahlen erhalte ich ungefähr 100 Datensätze pro Sekunde, was für diese Abfrage extrem langsam ist, da fast eine Million Datensätze zurückgegeben werden sollten.
Ich habe überprüft, ob jede Tabelle einen Primärschlüssel hat, das tun sie alle. Sie alle haben Indizes UND Statistiken für ihre wichtigen Spalten, wie die in diesen WHERE-Klauseln und die in der JOIN-Klausel. Die einzige Tabelle, in der weder ein Primärschlüssel noch ein Index definiert ist, ist die temporäre Tabelle, aber es ist auch nicht das Problem, da sie nicht mit den langsamen Unterauswahlen zusammenhängt, und wie bereits erwähnt, läuft sie ohne Unterauswahlen einwandfrei.
Ohne diese TOP 1
gibt mehr als ein Ergebnis zurück und löst einen Fehler aus.
Hilfe, jemand?
EDIT :
Der Ausführungsplan sagte mir also, dass mir ein Index fehlte. Ich habe es erstellt und einige der anderen Indizes neu erstellt. Nach einer Weile wurden sie vom Ausführungsplan verwendet, und die Abfrage wird jetzt schnell ausgeführt. Das einzige Problem ist, dass es mir nicht gelingt, dies auf einem anderen Server für dieselbe Abfrage erneut durchzuführen. Meine Lösung lautet also TIPP, welchen Index SQL Server verwenden wird.
Ich denke, bei einer Abfrage von Millionen Datensätzen muss man Dinge wie OUTER JOINS
Vermeiden. Ich schlage vor, Sie verwenden UNION ALL
Anstelle von LEFT JOIN
. Solange ich denke, dass CROSS APPLY
Effizienter ist als eine Unterabfrage in der select-Klausel, werde ich die von Conard Frix geschriebene Abfrage ändern, was ich für richtig halte.
jetzt: Als ich anfing, Ihre Abfrage zu ändern, bemerkte ich, dass Sie eine WHERE-Klausel haben, die besagt: JoinedTable.WhereColumn IN (1, 3)
. In diesem Fall wird die Bedingung falsch, wenn das Feld null ist. Warum verwenden Sie dann LEFT JOIN, während Sie Zeilen mit Nullwerten filtern? Ersetzen Sie einfach LEFT JOIN
durch INNER JOIN
, ich garantiere, dass es schneller wird.
über INDEX :
bitte beachten Sie, dass Sie beispielsweise einen Index für eine Tabelle haben
table1(a int, b nvarchar)
und Ihr Index ist:
nonclustered index ix1 on table1(a)
und du willst so etwas machen:
select a,b from table1
where a < 10
sie haben die Spalte b
nicht in Ihren Index aufgenommen. Was passiert also?
wenn SQL-Server Ihren Index verwendet, muss er im Index mit dem Namen "Index Seek" suchen und dann auf die Haupttabelle verweisen, um die Spalte b
mit dem Namen "Nachschlagen". Dieser Vorgang kann viel länger dauern als das Scannen der Tabelle selbst: "Tabellenscan".
basierend auf den Statistiken, über die SQL Server verfügt, wird Ihr Index in solchen Situationen möglicherweise überhaupt nicht verwendet.
überprüfen Sie daher zunächst Execution Plan
, um festzustellen, ob der Index überhaupt verwendet wird.
wenn ja oder nein, ändern Sie Ihren Index so, dass alle von Ihnen ausgewählten Spalten enthalten sind. sag wie:
nonclustered index ix1 on table1(a) include(b)
in diesem Fall wird Look Up nicht benötigt und Ihre Abfrage wird viel schneller ausgeführt.
Es ist die Unterauswahl in Ihrer Spaltenauswahl, die die langsame Rückkehr verursacht. Sie sollten versuchen, Ihre Unterauswahl in linken Verknüpfungen zu verwenden, oder eine abgeleitete Tabelle verwenden, wie ich unten definiert habe.
Verwenden von Linksverknüpfungen zu zwei Instanzen der dritten Tabelle
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
ThirdTable.Col1 AS ThirdTableColumn1,
ThirdTable2.Col1 AS ThirdTableColumn2
FROM #TempTable as TempTable
LEFT JOIN JoinedTable ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND
TempTable.PKColumn 2 = JoinedTable.PKColumn2)
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
WHERE
JoinedTable.WhereColumn IN (1, 3)
Verwenden einer abgeleiteten Tabelle
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
DerivedTable.Col1,
DerivedTable.Col2,
DerivedTable.ThirdTableColumn1,
DerivedTable.ThirdTableColumn2
FROM #TempTable as TempTable
LEFT JOIN (SELECT
JoinedTable.PKColumn2,
JoinedTable.Col1,
JoinedTable.Col2,
JoinedTable.WhereColumn,
ThirdTable.Col1 AS ThirdTableColumn1,
ThirdTable2.Col1 AS ThirdTableColumn2
FROM JoinedTable
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn)
DerivedTable ON (TempTable.PKColumn1 = DerivedTable .PKColumn2 AND
TempTable.PKColumn2 = DerivedTable.PKColumn2)
WHERE
DerivedTable.WhereColumn IN (1, 3)
Versuchen Sie stattdessen ein Kreuz anzuwenden
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
ThirdTableColumn1.col1,
ThirdTableColumn2.col1
FROM
#TempTable as TempTable
LEFT JOIN
JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND
TempTable.PKColumn 2 = JoinedTablePKColumn2)
CROSS APPLY
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1
CROSS APPLY (
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
WHERE
JoinedTable.WhereColumn IN (1, 3)
Sie können auch CTEs und row_number oder eine Inline-Abfrage mit MIN verwenden
Verschieben Sie die JOIN-Bits aus dem Hauptteil der Klausel und setzen Sie sie als Unterauswahl. Wenn Sie es in den Abschnitt WHERE and JOIN verschieben, müssen Sie nicht immer wieder TOP 1 AUSWÄHLEN, was meiner Meinung nach der Grund für die Langsamkeit ist. Wenn Sie dies überprüfen möchten, überprüfen Sie den Ausführungsplan.
Die ThirdTable
-Referenzen (in Ihrem Beispiel Unterauswahlen) benötigen dieselbe Indexaufmerksamkeit wie jeder andere Teil einer Abfrage.
Unabhängig davon, ob Sie Unterauswahl verwenden:
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
LINKE VERBINDUNGEN (wie von John Hartsock vorgeschlagen):
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
CROSS APPLY (wie von Conrad Frix vorgeschlagen):
CROSS APPLY
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1
CROSS APPLY (
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2
Sie müssen sicherstellen, dass covering indexes
Für ThirdTable.SomeColumn
Und ThirdTable.SomeOtherColumn
Definiert sind und die Indizes eindeutig sind. Dies bedeutet, dass Sie die ThirdTable
- Referenzen weiter qualifizieren müssen, um die Auswahl mehrerer Zeilen zu vermeiden und die Leistung zu verbessern. Die Auswahl von sub selects
, LEFT JOIN
Oder CROSS APPLY
Ist erst dann von Bedeutung, wenn Sie die Selektivität für ThirdTable.SomeColumn
Und ThirdTable.SomeOtherColumn
Durch Hinzufügen von mehr verbessern Spalten, um eine eindeutige Selektivität zu gewährleisten. Bis dahin gehe ich davon aus, dass Ihre Leistung weiterhin darunter leiden wird.
Das Thema covering index
Wird von Maziar Taheri gut vorgestellt. Obwohl ich seine Arbeit nicht wiederhole, betone ich die Notwendigkeit, die Verwendung von Deckungsindizes zu Herzen zu nehmen.
Kurz gesagt: Verbessern Sie die Selektivität für die Abfragen [oder Verknüpfungen ThirdTable.SomeColumn
Und ThirdTable.SomeOtherColumn
, Indem Sie verwandte Spalten in der Tabelle hinzufügen, um eine eindeutige Zeilenübereinstimmung sicherzustellen. Wenn dies nicht möglich ist, treten weiterhin Leistungsprobleme auf, da der Motor damit beschäftigt ist, Reihen einzuziehen, die anschließend weggeworfen werden. Dies wirkt sich auf Ihre E/A, CPU und letztendlich auf den Ausführungsplan aus.