Mein Problem (oder zumindest die Fehlermeldung) ist dem Abfrageprozessor sehr ähnlich, da ihm die internen Ressourcen ausgehen - extrem lange SQL-Abfrage .
Mein Kunde arbeitet mit einer SQL-Auswahlabfrage, die eine Where-Klausel mit genau 100.000 Einträgen enthält.
Die Abfrage schlägt mit Fehler 8632 und Fehlermeldung fehl
Interner Fehler: Ein Ausdrucksdienstlimit wurde erreicht. Bitte suchen Sie in Ihrer Anfrage nach potenziell komplexen Ausdrücken und versuchen Sie diese zu vereinfachen.)
Ich finde es sehr merkwürdig, dass diese Fehlermeldung genau bei 100.000 Einträgen ausgegeben wird, daher frage ich mich, ob dies ein konfigurierbarer Wert ist. Ist dies der Fall und falls ja, wie kann ich diesen Wert auf einen höheren Wert erhöhen?
Auf MSDN gibt es den Vorschlag, die Abfrage neu zu schreiben, aber ich möchte dies vermeiden.
In der Zwischenzeit habe ich herausgefunden, dass die Liste der Einträge, über die ich spreche, natürliche Zahlen enthält, von denen einige sequenziell zu sein scheinen (so etwas wie (1,2,3,6,7,8,9,10,12, 13,15,16,17,18,19,20).
Dies macht die SQL-WHERE-Klausel ungefähr so:
where entry in (1,2,3,6,7,8,9,10,12,13,15,16,17,18,19,20)
Ich könnte dies umwandeln in:
where (entry between 1 and 3) OR
(entry between 6 and 10) OR
(entry between 12 and 13) OR
(entry between 15 and 20)
Kann dies verkürzt werden durch:
where entry in (1,...,3,6,...,10,12,13,15,...,20)
...oder etwas ähnliches? (Ich weiß, es ist ein langer Weg, aber es würde Software-Updates einfacher und lesbarer machen.)
Zu Ihrer Information: Die Daten in der where-Klausel sind das Ergebnis einer Berechnung, die in einer anderen Tabelle durchgeführt wurde: Zuerst werden die Einträge dieser Tabelle gelesen und zu Beginn gefiltert SQL), das Ergebnis dieser zusätzlichen Verarbeitung ist eine stärkere Filterung, und das Ergebnis wird in der where-Klausel verwendet. Da es unmöglich war, die vollständige Filterung in SQL zu schreiben, wurde die erwähnte Methode verwendet. Offensichtlich kann sich der Inhalt der where-Klausel bei jeder Verarbeitung ändern, weshalb eine dynamische Lösung erforderlich ist.
quelle
WHERE IN
diese Art der Bereichssyntax wird nicht unterstützt. Auch sollte esWHERE () OR () OR ()
nicht UND sein. Um den Vorschlag von Brent zu verwenden, müssen Sie jedoch nicht die gesamte Abfrage ändern, sondern können dies einfach tunWHERE IN (SELECT myID FROM #biglist)
. Und es kann#biglist
sich entweder um einen echten (permanenten) Tisch oder einen temporären Tisch handeln, den Sie im Handumdrehen erstellen.Antworten:
Um nach mehr als 100.000 Werten zu suchen, fügen Sie diese stattdessen in eine temporäre Tabelle ein, und zwar eine Zeile pro Wert, nach dem Sie suchen. Verbinden Sie dann Ihre Abfrage mit dieser temporären Tabelle zum Filtern.
Etwas mit mehr als 100.000 Werten ist kein Parameter - es ist eine Tabelle. Ziehen Sie die Zehn-Prozent-Regel von Swart in Betracht, anstatt darüber nachzudenken, das Limit zu erhöhen : Wenn Sie sich 10% eines SQL Server-Limits nähern, werden Sie wahrscheinlich eine schlechte Zeit haben.
quelle
Wenn Sie die App dennoch ändern möchten, sollten Sie dies in Betracht ziehen
(a) Verwenden eines TVP für den gesamten Wertesatz - Sie würden ein
DataTable
in C # erstellen und es mit in eine gespeicherte Prozedur übergebenStructuredType
, wie ich hier demonstriere . (Hoffentlich sind 100.000 Einträge nicht normal, da die Skalierbarkeit ein Problem sein kann, egal welchen Ansatz Sie verwenden.)oder
(b) Verwenden eines TVP, um die oberen und unteren Grenzen der Bereiche zu überschreiten, und Schreiben eines etwas anderen Joins (danke @ypercube).
quelle
Nein, es ist nicht konfigurierbar und Sie können dies nicht auf einen höheren Wert erhöhen.
In dem von Ihnen erwähnten MSDN-Artikel und in anderen Artikeln wird eine Problemumgehung vorgeschlagen. Ich erwähnte hier zwei, aber Sie können nach mehr suchen.
quelle
Nur meine 2 ¢ zur Verkürzung der Abfragebedingung: -
Wenn Sie in der Lage sind, alle möglichen Werte von
entry
im Voraus zu bestimmen , wäre es möglich, wenn Sie die Ergänzung Ihrer Abfrage nehmen?Vor
Nach
quelle
Es ist schwer herauszufinden, was Sie erreichen möchten, ohne die Abfrage tatsächlich sehen zu können. Wir gehen jedoch davon aus, dass Ihre Abfrage nur zwei Tabellen benötigt, eine mit den Daten und eine mit den Werten, die Sie vermeiden möchten:
where entry in (<list of 100.000 values>)
ist sowohl aus Effizienz- als auch aus Wartungssicht unverschämt schrecklich.where entry in (select whatever from table)
ist kaum so schrecklich wie das vorhergehende. Abhängig von der Eigenart beider Tabellen und der SQL-Engine ist die Leistung trotz des Hornhautkrebses jedoch möglicherweise in Ordnung. (Könnte in Oracle in Ordnung sein, wird niemals in MySQL sein, kann mich nicht an MSSQL erinnern).Meiner Meinung nach (ohne die Abfrage überhaupt zu kennen) sollten Sie die Abfrage wie folgt umschreiben:
Wenn diese 100.000 Werte immer gleich sind und nicht auf den Rest der Abfrage angewiesen sind, sollten Sie diese Werte in eine Tabelle (table_fixed_values) hochladen und verwenden
Wenn diese 100.000 Werte nicht identisch sind, muss es eine Art Logik geben, um diese 100.000 Werte zu ermitteln. Diese Logik sollten Sie in die
ON
der vorherigen Abfrage einbetten .quelle
LEFT JOIN table_fixed_values ON A.entry=B.entry WHERE B.entry IS NOT NULL
und nicht das Äquivalent, einfacher und leichter zu lesenINNER JOIN table_fixed_values ON A.entry=B.entry
?