Das Aktualisieren einer Tabelle mit Millionen von Datensätzen dauerte 4 Tage

12

Ich aktualisiere gerade eine Tabelle mit Millionen von Datensätzen. Es sind 4 Tage vergangen und die Abfrage wird noch ausgeführt.

Ich habe die Aktivitätsüberwachung überprüft und festgestellt, dass die Abfrage ausgeführt wird.

Im Ereignisprotokoll gibt es überhaupt keine Fehler.

Leistung weise:

  • Tempdb in Festplatte A (850 GB freier Speicherplatz)
  • Datenbankdatei auf Datenträger B (750 GB freier Speicherplatz)
  • 16 GB RAM

Bitte schlagen Sie mir vor, was ich tun soll.

Die Abfrage

UPDATE
    dbo.table1
SET 
    costPercentage = ISNULL(t2.PaymentIndex, 1.0),
    t2.TopUp_Amt = (ISNULL(t2.PaymentIndex, 1.0) - 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00),
    Total_Tariff_Inc_t2 = ISNULL(t2.PaymentIndex, 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00)
FROM
    dbo.table2 t2
WHERE
    LEFT(dbo.test1.procodet, 3) = LEFT(t2.ProviderCode, 3) COLLATE database_default 
Glücklich
quelle

Antworten:

3

Es gibt ein interessantes Detail zu dieser Abfrage, das ich zuerst nicht entdeckt habe. Dank der Antwort von Fabricio Araujo sehe ich es jetzt: Sie greifen auf zwei Tabellen zu. Ich habe diese Art der Verwendung der Update-Anweisung noch nie gesehen und rate davon ab, sie zu verwenden. Ich empfehle die Verwendung der intuitiveren Join-Syntax gemäß der Antwort von Fabricio.

Die wahrscheinliche Ursache ist, dass der Join zwischen den beiden Tabellen eine extreme Anzahl von Zeilen erzeugt. Dies kann passieren, wenn der LEFT(col, 3)Ausdruck doppelte Werte erzeugt. Wenn 10 Duplikate erstellt werden, führt dies zu 100000 x 100000 = 10000000000 Zeilen im Verknüpfungsergebnis.

Ich denke nicht, dass die Indizierung hier eine Rolle spielt. SQL Server kann diesen nicht indizierten Join problemlos mit einem Hash oder einem Merge-Join auflösen. Dauert nicht 4 Tage.

Die andere Ursache wäre wahrscheinlich eine Unterschätzung der Kardinalität der Join-Eingaben oder -Ausgaben. SQL Server hat möglicherweise einen Loop-Join ausgewählt.

Da dies immer noch Spekulation ist, empfehle ich Ihnen, den Abfrageplan zu veröffentlichen, der Aufschluss über dieses Problem gibt.

usr
quelle
8

Bei dieser Abfrage müssen Sie jede Zeile in der Tabelle überprüfen, da

  • Ich denke procodet oder ProviderCode sind nicht indiziert
  • Selbst wenn sie indiziert wurden, haben Sie ein LEFT, das eine Funktion auf einem WHERE-Prädikat ist
  • Und Sie haben auch COLLATE, was effektiv eine Funktion auf einem WHERE-Prädikat ist

"Eine Funktion auf einem WHERE-Prädikat" bedeutet, dass keine Indizes verwendet werden

Wenn Sie einen Stapel erstellen (z. B. UPDATE TOP (10000) ... AND costPercentage IS NULL), benötigen Sie einen Index für costPercentage, und dies setzt voraus, dass Sie ihn festlegen .

Die einzigen Lösungen, die ich sehe, sind

  • Füllen Sie eine neue Tabelle stapelweise auf, beispielsweise basierend auf dem Primärschlüssel
  • Erstellen Sie indizierte, berechnete Spalten, um die Ausdrücke LEFT und COLLATE auszublenden, und führen Sie dann die Aktualisierung aus
gbn
quelle
@ gbn .. danke das ist eine großartige idee .. aber da die daten in millionen sind, wird dieser prozess einige zeit in Anspruch nehmen .... ich dachte, gibt es vielleicht eine möglichkeit, den fortschritt der abfrage herauszufinden?
Lucky
1
Warum würde es 4 Tage dauern, um "Millionen" Zeilen zu scannen? Egal wie groß und stark indiziert die Zeilen sein mögen, das sollte nicht 4 Tage dauern. Die Wurzel des Problems ist noch unbekannt.
usr
1
Wenn Sie regelmäßig mit großen Datenmengen arbeiten, erhalten Sie dafür einen geeigneten Server? Legen Sie die Daten auf eine SSD usw.
TomTom
1
@Lucky sicher. Ich habe die Antwort angesprochen. Es stimmt etwas nicht, was wir noch nicht gefunden haben. Es ist nicht die Abfrage für sich oder die Hardware. Das würde niemals 4 Tage dauern.
usr
3
Da die Abfrage einen 3-Zeichen-Teil einer Spalte mit einem 3-Zeichen-Teil einer anderen Spalte verknüpft, enthält das Ergebnis höchstwahrscheinlich Duplikate. Dies ist viel schlimmer, als nur Millionen von Zeilen zu aktualisieren. Ich wette, es ist in Milliardenhöhe durch einen Arbeitstisch zu scannen.
Datum
4

Ändern Sie zunächst die Abfrage in:

UPDATE t1
SET 
    costPercentage = ISNULL(t2.PaymentIndex, 1.0),
    t2.TopUp_Amt = (ISNULL(t2.PaymentIndex, 1.0) - 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00),
    Total_Tariff_Inc_t2 = ISNULL(t2.PaymentIndex, 1.0)
    * ISNULL(dbo.table1.Initial_Tariff_Amt, 0.00)
FROM
  dbo.table1 t1
  inner join dbo.table2 t2
    on LEFT(t1.procodet, 3) = LEFT(t2.ProviderCode, 3) COLLATE database_default 

Wie in Jeff Modens erstem Beitrag in dieser Diskussion erwähnt , ist Ihre Anfrage der sehr ähnlich, die er vor dem "Halloween-Effekt" gewarnt hat.

Danach müssen diese LEFT-Ausdrücke indiziert werden. Die Antwort von gbn zeigt Ihnen, wie das geht.

Fabricio Araujo
quelle