Selezionare l'elaborazione dell'istruzione prima di dove la clausola in vista
-
12-11-2019 - |
Domanda
Ho una dichiarazione che seleziona la sottostringa di un charindex come segue:
SELECT SUBSTRING(StringField, 5, CHARINDEX('ABC', StringField) - 5)...
WHERE
CHARINDEX('ABC', StringField) > 5
.
Quando eseguo la dichiarazione di cui sopra in una query di selezione, i risultati vengono restituiti bene.Quando eseguo la dichiarazione di cui sopra in una vista indicizzata su uno schema, ottengo questo errore:
Invalid length parameter passed to the LEFT or SUBSTRING function
.
Per risolvere il problema scriverò una funzione per ottenere il massimo del charindex e 0 per rimuovere le possibilità di un valore negativo.Ma qualcuno sa perché la clausola WHERE non filtrasse l'istruzione SELECT?
Soluzione
Perché l'ordine delle operazioni dietro le quinte in una query non è garantita.
Suppongo se si controlla il piano di esecuzione vedrai che sta facendo entrambi i controlli in parallelo - questo perché nessuna operazione può utilizzare un indice!
SQL ha bisogno di caricare ogni riga di quel campo in memoria comunque, quindi lo sta elaborando per entrambi i criteri allo stesso tempo.
È possibile provare WITH (MAXDOP(1))
come suggerimento di query per vedere se ciò mantiene il problema da comparare, oppure è possibile eseguire una sottosecuzione per forzare l'ordine di esecuzione:
.
SELECT SUBSTRING(StringField, 5, CHARINDEX('ABC', StringField) - 5)...
FROM (
SELECT Stringfield
FROM Table
WHERE CHARINDEX('ABC', StringField) > 5
) as [X]
Ho avuto una questione simile una volta quando stavo controllando di vedere se un campo è stato numerico utilizzando PATINDEX
e una delle colonne in my panorama è stata convertendolo in un int
- ho ricevuto errori di conversione perché il motore stava convertendo ogni riga dal mioI filtri non erano sargabili.
Altri suggerimenti
Questa è una brutta soluzione alternativa ma potresti fare qualcosa come questo forse:
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;
.
Entrambi rendimento:
---
AAA
.
Ma sospetto che quest'ultimo sarà consentito in vostra vista.È brutto ma sfortunatamente a meno che non si scarica i dati filtrati in un #temp da prima (o provare a vedere se MAXDOP
rimuove in modo affidabile il problema) che non avrai molto controllo su ordinazione di elaborazione.
Un'altra idea è di provare a mettere una colonna calcolata nella tabella (ma non sono sicuro che ti aiuterà se si sta tentando di creare una vista indicizzata - ci possono ancora essere complicazioni).O per utilizzare un indice filtrato con quell'espressione, invece di una vista indicizzata.Ci potrebbero essere diverse "soluzioni" se sappiamo cosa è destinata la vista indicizzata per realizzare e quale versione di SQL Server si sta utilizzando.