Ich hatte dieses Problem vor langer Zeit, fand eine Problemumgehung, die zu mir passte, und vergaß es.
Aber jetzt gibt es diese Frage zu SO, also bin ich bereit, dieses Problem anzusprechen.
Es gibt eine Ansicht, die einige Tabellen auf sehr einfache Weise verbindet (Bestellungen + Bestellpositionen).
Bei einer Abfrage ohne where
Klausel gibt die Ansicht mehrere Millionen Zeilen zurück.
Niemand nennt es jedoch jemals so. Die übliche Abfrage ist
select * from that_nasty_view where order_number = 123456;
Dies gibt ungefähr 10 Datensätze von 5 m zurück.
Wichtig: Die Ansicht enthält eine Fensterfunktion rank()
, die genau nach dem Feld unterteilt ist, mit dem die Ansicht immer abgefragt wird:
rank() over (partition by order_number order by detail_line_number)
Wenn diese Ansicht nun genau wie oben gezeigt mit Literalparametern in der Abfragezeichenfolge abgefragt wird, werden die Zeilen sofort zurückgegeben. Der Ausführungsplan ist in Ordnung:
- Indexsuche für beide Tabellen unter Verwendung der Indizes auf
order_number
(gibt 10 Zeilen zurück). - Berechnen von Fenstern über dem zurückgegebenen winzigen Ergebnis.
- Auswählen.
Wenn die Ansicht jedoch parametrisiert aufgerufen wird, werden die Dinge unangenehm:
Index scan
auf allen Tabellen ohne Indizes. Gibt 5 m Zeilen zurück.- Riesige Verbindung.
- Berechnen von Fenstern über alle
partition
s (ca. 500.000 Fenster). Filter
10 Reihen aus 5m nehmen.- Wählen
Dies geschieht in allen Fällen, wenn Parameter beteiligt sind. Es kann SSMS sein:
declare @order_number int = 123456;
select * from that_nasty_view where order_number = @order_number;
Es kann sich um einen ODBC-Client wie Excel handeln:
select * from that_nasty_view where order_number = ?
Oder es kann ein anderer Client sein, der Parameter und keine SQL-Verkettung verwendet.
Wenn die Fensterfunktion aus der Ansicht entfernt wird, wird sie einwandfrei ausgeführt, unabhängig davon, ob sie mit Parametern abgefragt wird oder nicht.
Meine Problemumgehung bestand darin, die fehlerhafte Funktion zu entfernen und zu einem späteren Zeitpunkt erneut anzuwenden.
Aber was gibt es? Ist es wirklich ein Fehler, wie SQL Server 2008 mit Fensterfunktionen umgeht?
order_number
ist kein Primärschlüssel.int not null
In beiden Tabellen befindet sich ein nicht gruppierter Index.OPTION (RECOMPILE)
?Antworten:
Dies scheint ein langjähriges Problem zu sein, das in der einen oder anderen Form immer wieder auftaucht und in SQL Server 2012 immer noch vorhanden ist.
Einige Beiträge, die darüber diskutieren, sind
Alle aktuellen Versionen von SQL Server bis einschließlich 2012 können den Filter für eine Partitionierungsgruppe nicht über das Sequenzprojekt hinaus für ein parametrisiertes Prädikat verschieben, es sei denn, es
option(recompile)
wird verwendet (falls 2008+).Eine Alternative zum
recompile
Hinweis wäre, die Abfrage neu zu schreiben, um eine parametrisierte Inline-TVF zu verwenden, wie von @ a1ex07 vorgeschlagen.quelle
Ich würde versuchen, die Ansicht durch udf mit Tabellenwert zu ersetzen. Auf diese Weise werden zuerst Datensätze gefiltert und dann die Fensterfunktion angewendet. Diese Funktion kann Tabellenparameter akzeptieren , so dass Sie mehrere passieren kann
order_number
hineinquelle
SELECT * FROM my_funct(12345)
it will filter records first, and then apply window function
ist falsch. Es gibt keine deterministische Reihenfolge für die Hinrichtung