it-swarm.com.de

SQL Select benötigt zu viel Zeit für die Ausführung

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.

9
Smur

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.

7
Maziar Taheri

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)
6
John Hartsock

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

2
Conrad Frix

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.

2

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.

2
Robert Miller