CTE-Spalte verursachte einen Überlauf - Nur Bestellen nach!

7
;WITH
cte_Date ( DateCode_FK ) AS (
    SELECT  DATEADD( DAY, 
                1 - ROW_NUMBER() OVER ( 
                        ORDER BY so1.object_id ),
                GETDATE() )
    FROM    sys.objects so1
    CROSS APPLY sys.objects so2 )
SELECT  TOP 10 d.DateCode_FK
FROM    cte_Date d
ORDER BY d.DateCode_FK DESC;

Keine übermäßig interessante Abfrage, aber ich erhalte eine Fehlermeldung, wenn ich sie mit der folgenden ORDER BYKlausel ausführe :

Nachricht 517, Ebene 16, Status 1, Zeile 4

Das Hinzufügen eines Werts zu einer 'datetime'-Spalte verursachte einen Überlauf.

Ohne die ORDER BYKlausel läuft es jedoch einwandfrei. Wenn ich die Abfrage in anderen Katalogen ausführe, die in derselben Instanz auf demselben Server enthalten sind, wird die Abfrage außerdem mit oder ohne ORDER BYKlausel ausgeführt.

Ich habe mir die Konfigurationsoptionen und Kompatibilitätsstufen zwischen dem betroffenen Katalog und einem Katalog angesehen, in dem die Abfrage wie erwartet ausgeführt wird, aber nichts gefunden, was den Unterschied rechtfertigen könnte. Ist noch jemand auf ein ähnliches Problem gestoßen? Ich kann es jetzt umgehen, müsste aber im Idealfall in der Lage sein, das Problem zu beheben, was auch immer es ist.

Möglicher Hinweis - Wenn Sie eine relativ große Anzahl von Objekten in einem Katalog haben (> 5000), können Sie den Fehler möglicherweise reproduzieren ... Dies tritt in meinem größten Katalog auf und es scheint, dass wenn ich ein TOP in einfüge Der CTE, das ORDER BY-Problem, verschwindet.

Avarkx
quelle

Antworten:

9

SQL Server garantiert nicht das Timing oder die Anzahl der Auswertungen für skalare Ausdrücke. Dies bedeutet, dass eine Abfrage, die abhängig von der Reihenfolge der Operationen in einem Ausführungsplan einen Fehler auslösen kann , dies zur Laufzeit tun kann (oder auch nicht).

Das Skript verwendet, CROSS APPLYwo CROSS JOINwahrscheinlich beabsichtigt war, aber der Punkt ist, dass die potenzielle Anzahl von Zeilen, über die ROW_NUMBERberechnet wird, von der Größe des sys.objectsKreuzprodukts abhängt .

Bei einer Datenbank mit ausreichend Objekten ist ein Überlauf des DATEADDErgebnisses ein zu erwartendes Risiko. Dies ist beispielsweise mit der AdventureWorks- Beispieldatenbank reproduzierbar , die 637 Einträge enthält sys.objects. Die Größe des Kreuzprodukts beträgt 637 · 637 = 405.769; Folgendes löst einen Überlauffehler aus:

SELECT DATEADD(DAY, 1 - 405769, GETDATE());

Man könnte argumentieren, dass es nicht notwendig ist, das Ergebnis eines Ausdrucks für Zeilen zu materialisieren , die nicht zurückgegeben werden (und daher keinen Fehler auslösen), aber so funktionieren die Dinge heute nicht.

Erwägen:

  • Der höchste Wert ROW_NUMBERgibt den niedrigsten Wert für DateCode_FKden DATEADD(DAY, 1 - ROW_NUMBER()...Ausdruck an
  • Die Präsentation ORDER BYistDateCode_FK DESC
  • Es sind nur die ersten 10 Zeilen in Präsentationsreihenfolge erforderlich

Wenn das Optimierungsprogramm eine Logik enthält, die besagt, dass niedrigere Zeilennummern zu höheren DateCode_FK Werten führen, wäre eine explizite Sortierung nicht erforderlich. Nur zehn Zeilen müssten durch den Ausführungsplan fließen. Die zehn niedrigsten Zeilennummern ergeben garantiert die zehn höchsten DateCode_FKWerte.

Unabhängig davon, selbst wenn eine Sortierung vorhanden ist, sollte SQL Server keinen Fehler auslösen, da keine der zehn tatsächlich zurückgegebenen Zeilen mit einem Überlauffehler verbunden ist. Wie ich oben sagte, "so funktionieren die Dinge heute nicht" .


Eine alternative Formulierung, die den Fehler vermeidet (obwohl dies immer noch nicht garantiert ist - siehe meine einleitende Bemerkung), macht die Zeilennummerierung deterministisch und verwendet CROSS JOIN:

WITH cte_Date (DateCode_FK) AS
(
    SELECT TOP (10)
        DATEADD
        (
            DAY, 
            1 - ROW_NUMBER() OVER ( 
                ORDER BY 
                    so1.[object_id],
                    so2.[object_id]),
            GETDATE()
        )
    FROM sys.objects AS so1
    CROSS JOIN sys.objects AS so2
    ORDER BY
        ROW_NUMBER() OVER (
            ORDER BY 
                so1.[object_id],
                so2.[object_id]) ASC
)
SELECT TOP (10) -- Redundant TOP
    d.DateCode_FK
FROM cte_Date AS d
ORDER BY 
    d.DateCode_FK DESC;

Fügen Sie den Plan ein

Planen

Ergebnisse

Paul White 9
quelle
1

Urlaubskater, denke ich.

Das ORDER BYim externen Teil der Abfrage erzwingt manchmal das Erstellen der vollständigen Liste, um sie korrekt zu ordnen. Wenn mehr als 5.000 Zeilen zurückgegeben werden sys.objects, DATETIMEläuft der Datentyp über, während versucht wird, Daten zu erstellen, die älter als 25 MM sind.

Avarkx
quelle