Запретить переписывание запросов в SQL Server - PullRequest
3 голосов
/ 16 февраля 2010

У меня есть столбец TypeCode varchar (20), который имеет значения, такие как «1», «2», «3», «FOO», «BAR». Мне нужно найти максимальное целое число, которое меньше значения параметра. Примерно так:

select max(TypeCode) TypeCode
    from table1 a
    left join table2 b on b.table1id = a.id
        and b.TypeCode not in ('FOO', 'BAR')
    where b.TypeCode < @MaxType

, который работает большую часть времени, но в некоторых запросах SQL Server решает преобразовать его в нечто подобное (в соответствии с планом запроса).

select max(TypeCode) TypeCode
    from table1 a
    left join table2 b on b.table1id = a.id
        and b.TypeCode < @MaxType
        and b.TypeCode not in ('FOO', 'BAR')

Этот запрос, очевидно, вызывает следующую ошибку:

Conversion failed when converting the varchar value 'FOO' to data type int.

Я попытался создать представление таблицы 2 без значений 'FOO' и 'BAR' и вместо этого присоединиться к представлению, но план запроса остается прежним.

Знаете ли вы, как оптимизатор может изменить запрос?

PS: я знаю, что дизайн таблицы не самый лучший, но это устаревшая база данных, и я не могу ее изменить.

Ответы [ 2 ]

2 голосов
/ 16 февраля 2010

Настоящая проблема здесь в том, что вы комбинируете семантику символов и целых в одном запросе.

Есть три возможных решения, о которых я могу подумать:

  1. Измените параметр @MaxType на varchar(9). Если вы сравниваете только однозначные числа, алфавитный порядок в порядке. В противном случае это не сработает.

  2. Используйте в выражении WHERE конструкцию CASE или ISNULL(NULLIF(...)). Это будет работать, но это не sargable и заставит оптимизатор игнорировать любые индексы, которые вы имеете в коде типа. Не отлично.

  3. Создает постоянный, вычисляемый столбец целых чисел, допускающий значение NULL (т. Е. TypeCodeID), и индексирует его отдельно. Поместите CASE в качестве выражения столбца. Это займет некоторое дополнительное пространство для данных / индексов, но если вам нужна хорошая производительность, это лучший способ. Тогда вместо записи NOT IN ('Foo', 'Bar') вы можете просто написать первое условие (TypeCode < @MaxType), потому что строки с Foo и Bar в столбце TypeCode будут иметь NULL в новом столбце TypeCodeID.

Полагаю, есть и четвертый ответ, который должен изменить ваш дизайн, и это будет лучшей идеей, если это возможно. Если столбец может содержать символьные данные, тогда вам не следует пытаться проводить численные сравнения. У меня есть сильное подозрение, что символьные данные существуют в этом столбце, потому что они поступают от пользователей и не проверены / не продезинфицированы должным образом, поэтому там есть такие значения мусора, как N/A или Unknown. Если это так, ваша БД действительно должна обеспечивать целостность данных, для этого и нужна СУБД. Я знаю, что вы говорите, что «не можете это изменить», но я не думаю, что этот ответ будет полным, если я не рекомендую против такой опасной практики.

0 голосов
/ 16 февраля 2010

Нет никакого способа отключить оптимизатор, и вы, вероятно, действительно не хотите этого. Лучшим решением, вероятно, является изменение запроса.

Вы, вероятно, можете сделать это с помощью оператора CASE - что-то вроде:

SELECT MAX(TypeCode) TypeCode
FROM table1 a
    LEFT JOIN table2 b ON b.table1id = a.id
WHERE CASE WHEN b.TypeCode IN ('FOO', 'BAR') THEN 99999999999 ELSE CAST(b.TypeCode AS int) END < @MaxType

Вам нужно проверить производительность, чтобы убедиться, что она приемлема, но она должна работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...