Выберите обработку оператора перед предложением Where в поле зрения
-
12-11-2019 - |
Вопрос
У меня есть оператор, который выбирает подстроку хариндекса следующим образом:
SELECT SUBSTRING(StringField, 5, CHARINDEX('ABC', StringField) - 5)...
WHERE
CHARINDEX('ABC', StringField) > 5
Когда я запускаю приведенный выше оператор в запросе выбора, результаты возвращаются нормально.Когда я запускаю приведенный выше оператор в индексированном представлении с привязкой к схеме, я получаю эту ошибку:
Invalid length parameter passed to the LEFT or SUBSTRING function
Чтобы решить проблему, я напишу функцию для получения максимального значения CharIndex и 0, чтобы исключить возможность отрицательного значения.Но кто-нибудь знает, почему предложениеwhere не отфильтровывает оператор выбора?
Решение
потому что порядок операций за кулисами в запросе не гарантируется.
Я предполагаю, что если вы проверяете план выполнения, вы увидите, что он делает оба проверки параллельно - это потому, что ни одна операция не может использовать индекс!
SQL необходимо в любом случае, чтобы загрузить каждую строку этого поля в память, поэтому она обрабатывает ее для обоих критериев одновременно.
Вы можете попробовать WITH (MAXDOP(1))
в качестве подсказки запроса, чтобы увидеть, сохраняет ли это проблему, иначе вы можете сделать подселение для заказа заказа в силу:
.
SELECT SUBSTRING(StringField, 5, CHARINDEX('ABC', StringField) - 5)...
FROM (
SELECT Stringfield
FROM Table
WHERE CHARINDEX('ABC', StringField) > 5
) as [X]
У меня была похожая проблема однажды, когда я проверял, было ли полем числовым с использованием PATINDEX
, и один из столбцов на мой взгляд был преобразовывать это на int
- у меня есть ошибки преобразования, потому что двигатель преобразует каждую строку с момента моегоФильтры не были саргабельными.
Другие советы
Это уродливый обходной путь, но вы могли бы сделать что-то вроде этого:
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;
Оба дают:
---
AAA
Но я подозреваю, что последнее, по вашему мнению, будет разрешено.Это некрасиво, но, к сожалению, если вы сначала не сбросите отфильтрованные данные в таблицу #temp (или не попытаетесь проверить, есть ли MAXDOP
надежно устраняет проблему) у вас не будет особого контроля над порядком обработки.
Другая идея — попробовать поместить в таблицу вычисляемый столбец (но я не уверен, что это поможет, если вы пытаетесь создать индексированное представление — все равно могут возникнуть сложности).Или использовать отфильтрованный индекс с этим выражением вместо индексированного представления.Может быть несколько «решений», если мы знаем, для чего предназначено индексированное представление и какую версию SQL Server вы используете.