Frage

Ich habe eine Anweisung, die den Teilstring eines Charindex wie folgt auswählt:

SELECT SUBSTRING(StringField, 5, CHARINDEX('ABC', StringField) - 5)...
WHERE 
CHARINDEX('ABC', StringField) > 5

Wenn ich die obige Anweisung in einer Auswahlabfrage ausführe, werden die Ergebnisse einwandfrei zurückgegeben.Wenn ich die obige Anweisung in einer schemagebundenen indizierten Ansicht ausführe, erhalte ich diesen Fehler:

Invalid length parameter passed to the LEFT or SUBSTRING function

Um das Problem zu lösen, werde ich eine Funktion schreiben, um das Maximum des CharIndex und 0 zu ermitteln, um die Möglichkeiten eines negativen Werts zu beseitigen.Aber weiß jemand, warum die where-Klausel die select-Anweisung nicht herausfiltern würde?

War es hilfreich?

Lösung

Weil die Reihenfolge der Vorgänge hinter den Kulissen in einer Abfrage nicht garantiert ist.

Ich vermute, wenn Sie den Ausführungsplan überprüfen, werden Sie feststellen, dass beide Überprüfungen parallel durchgeführt werden - dies liegt daran, dass keine Operation einen Index verwenden kann!

SQL muss sowieso jede Zeile dieses Felds in den Speicher laden, damit es für beide Kriterien gleichzeitig verarbeitet wird.

Du kannst es versuchen WITH (MAXDOP(1)) als Abfragehinweis, um zu sehen, ob das Problem dadurch nicht auftritt, oder Sie könnten eine Unterauswahl treffen, um die Ausführungsreihenfolge zu erzwingen:

SELECT SUBSTRING(StringField, 5, CHARINDEX('ABC', StringField) - 5)...
FROM (
      SELECT Stringfield 
      FROM Table 
      WHERE CHARINDEX('ABC', StringField) > 5
      ) as [X]

Ich hatte einmal ein ähnliches Problem, als ich überprüfte, ob ein Feld numerisch war mit PATINDEX und eine der Spalten aus meiner Sicht wandelte dies in eine um int - Ich habe Konvertierungsfehler erhalten, weil die Engine jede Zeile konvertiert hat, da meine Filter nicht sargfähig waren.

Andere Tipps

Dies ist eine hässliche Problemumgehung, aber Sie könnten vielleicht so etwas tun:

DECLARE @x TABLE(StringField VARCHAR(32));

INSERT @x SELECT 'ABC'
UNION ALL SELECT 'A'
UNION ALL SELECT 'AAAAAAAABC';

SELECT SUBSTRING(StringField, 5, CHARINDEX('ABC', StringField) - 5)
FROM @x
WHERE CHARINDEX('ABC', StringField) > 5;

SELECT SUBSTRING(StringField,
    CASE WHEN CHARINDEX('ABC', StringField) > 5 THEN 5 ELSE 1 END,
    CHARINDEX('ABC', StringField) - 
    CASE WHEN CHARINDEX('ABC', StringField) > 5 THEN 5 ELSE 0 END)
FROM @x
WHERE CHARINDEX('ABC', StringField) > 5;

Beide ergeben:

---
AAA

Aber ich vermute, dass letzteres aus Ihrer Sicht erlaubt sein wird.Es ist hässlich, aber leider, es sei denn, Sie speichern die gefilterten Daten zuerst in einer #temp-Tabelle (oder versuchen zu sehen, ob MAXDOP behebt das Problem zuverlässig), haben Sie nicht viel Kontrolle über die Reihenfolge der Verarbeitung.

Eine andere Idee ist, zu versuchen, eine berechnete Spalte in die Tabelle einzufügen (aber ich bin mir nicht sicher, ob dies hilfreich ist, wenn Sie versuchen, eine indizierte Ansicht zu erstellen - es kann immer noch zu Komplikationen kommen).Oder um einen gefilterten Index mit diesem Ausdruck anstelle einer indizierten Ansicht zu verwenden.Es könnte mehrere "Lösungen" geben, wenn wir wissen, was die indizierte Ansicht erreichen soll und welche Version von SQL Server Sie verwenden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top