Первоначальный вопрос был "Как мне параметризировать запрос ..."
Позвольте мне заявить прямо здесь, что это не ответ на первоначальный вопрос. Уже есть некоторые доказательства этого в других хороших ответах.
С учетом сказанного, пометьте этот ответ, опустите его вниз, отметьте его как не ответ ... делайте все, что вы считаете правильным.
См. Ответ Марка Брэкетта для предпочтительного ответа, который я (и 231 другие) проголосовали. Подход, изложенный в его ответе, позволяет: 1) эффективно использовать переменные связывания и 2) использовать предикаты, которые можно прояснить.
Выбранный ответ
Здесь я хочу остановиться на подходе, представленном в ответе Джоэла Спольски, ответ «выбран» в качестве правильного ответа.
Подход Джоэла Спольски умный. И он работает разумно, он будет демонстрировать предсказуемое поведение и предсказуемую производительность при заданных «нормальных» значениях и с нормативными крайними случаями, такими как NULL и пустая строка. И этого может быть достаточно для конкретного применения.
Но с точки зрения обобщения этого подхода давайте также рассмотрим более неясные угловые случаи, например, когда столбец Name
содержит подстановочный знак (как распознается предикатом LIKE.) Подстановочный знак, который я вижу наиболее часто используемым, - это %
(знак процента.). Итак, давайте разберемся с этим здесь и сейчас, а позже перейдем к другим случаям.
Некоторые проблемы с символом%
Рассмотрим значение имени 'pe%ter'
. (Для примеров здесь я использую буквальное строковое значение вместо имени столбца.) Строка со значением Name `pe pe ter будет возвращена запросом формы:
select ...
where '|peanut|butter|' like '%|' + 'pe%ter' + '|%'
Но эта же строка будет не возвращена, если порядок поисковых терминов обратный:
select ...
where '|butter|peanut|' like '%|' + 'pe%ter' + '|%'
Поведение, которое мы наблюдаем, немного странно. Изменение порядка поисковых терминов в списке приводит к изменению набора результатов.
Почти само собой разумеется, что мы не можем хотеть, чтобы pe%ter
соответствовал арахисовому маслу, независимо от того, насколько ему это нравится.
Неясный угловой чехол
(Да, я согласен, что это неясный случай. Вероятно, тот, который вряд ли будет проверен. Мы не ожидаем подстановочный знак в значении столбца. Мы можем предположить, что приложение предотвращает такое значение Но в моем опыте я редко видел ограничение базы данных, которое специально запрещало символы или шаблоны, которые считались бы подстановочными знаками справа от оператора сравнения LIKE
.
Ямочный ремонт
Один из подходов к исправлению этой дыры - избежать символа подстановки %
. (Для тех, кто не знаком с оператором escape, здесь приведена ссылка на документацию по SQL Server .
select ...
where '|peanut|butter|'
like '%|' + 'pe\%ter' + '|%' escape '\'
Теперь мы можем сопоставить буквальный%. Конечно, когда у нас есть имя столбца, нам нужно динамически экранировать шаблон. Мы можем использовать функцию REPLACE
, чтобы найти вхождения символа %
и вставить символ обратной косой черты перед каждым из них, например так:
select ...
where '|pe%ter|'
like '%|' + REPLACE( 'pe%ter' ,'%','\%') + '|%' escape '\'
Так что это решает проблему с подстановочным знаком%. Почти.
Побег побег
Мы понимаем, что в нашем решении появилась другая проблема. Спасательный персонаж. Мы видим, что нам также нужно будет избегать любых случаев появления экранирующего персонажа. На этот раз мы используем! в качестве escape-символа:
select ...
where '|pe%t!r|'
like '%|' + REPLACE(REPLACE( 'pe%t!r' ,'!','!!'),'%','!%') + '|%' escape '!'
Подчеркивание тоже
Теперь, когда мы находимся в процессе, мы можем добавить еще одну REPLACE
ручку с символом подчеркивания. И просто для удовольствия, на этот раз мы будем использовать $ в качестве escape-символа.
select ...
where '|p_%t!r|'
like '%|' + REPLACE(REPLACE(REPLACE( 'p_%t!r' ,'$','$$'),'%','$%'),'_','$_') + '|%' escape '$'
Я предпочитаю этот подход к экранированию, потому что он работает в Oracle и MySQL, а также в SQL Server. (Я обычно использую \ backslash в качестве escape-символа, так как это символ, который мы используем в регулярных выражениях. Но зачем ограничиваться соглашением!
Эти надоедливые скобки
SQL Server также позволяет обрабатывать символы подстановки как литералы, заключая их в квадратные скобки []
. Так что мы еще не закончили исправление, по крайней мере для SQL Server. Поскольку пары скобок имеют особое значение, нам также нужно избегать их. Если нам удастся правильно убрать скобки, то, по крайней мере, нам не придется беспокоиться о дефисе -
и карате ^
в скобках. И мы можем оставить любые экранированные символы %
и _
внутри скобок, так как мы в основном отключим специальное значение скобок.
Поиск подходящих пар скобок не должен быть таким сложным. Это немного сложнее, чем обрабатывать вхождения singleton% и _. (Обратите внимание, что недостаточно просто избежать всех вхождений скобок, потому что одиночная скобка считается литералом, и ее не нужно экранировать. Логика становится немного размытой, чем я могу справиться, не выполняя больше тестовых случаев .)
Встроенное выражение становится беспорядочным
Это встроенное выражение в SQL становится длиннее и уродливее. Мы, вероятно, можем заставить это работать, но небеса помогают бедной душе, которая приходит и должна расшифровать это. Как большой поклонник я для встроенных выражений, я склонен не использовать его здесь, главным образом потому, что я не хочу оставлять комментарий, объясняющий причину беспорядка, и извиняюсь за это.
Функция где?
Хорошо, поэтому, если мы не будем обрабатывать это как встроенное выражение в SQL, ближайшая альтернатива, которую мы имеем, - это пользовательская функция. И мы знаем, что это ничего не ускорит (если мы не можем определить индекс для него, как мы могли бы с Oracle.) Если нам нужно создать функцию, мы могли бы лучше сделать это в коде, который вызывает SQL заявление.
И эта функция может иметь некоторые различия в поведении, в зависимости от СУБД и версии. (Привет всем разработчикам Java, которые заинтересованы в взаимозаменяемости использования любого механизма базы данных.)
Знание предметной области
У нас могут быть специальные знания о домене для столбца (то есть набор допустимых значений, применяемых для столбца. Мы можем знать a priori , что значения, хранящиеся в столбце, никогда не будут содержать знак процента, знак подчеркивания или скобки. В этом случае мы просто включаем быстрый комментарий о том, что эти случаи покрыты.
Значения, хранящиеся в столбце, могут содержать символы% или _, но ограничение может потребовать экранирования этих значений, возможно, с использованием определенного символа, так что эти значения LIKE сравнения "безопасны". Опять же, быстрый комментарий о допустимом наборе значений и, в частности, о том, какой символ используется в качестве escape-символа, и он соответствует подходу Джоэла Спольски.
Но, при отсутствии специальных знаний и гарантии, для нас важно, по крайней мере, рассмотреть возможность обработки этих непонятных угловых случаев и решить, является ли поведение разумным и "в соответствии со спецификацией".
Другие вопросы перепросмотрены
Я полагаю, что другие уже в достаточной степени указали на некоторые из других обычно рассматриваемых проблемных областей:
SQL-инъекция (принимая то, что кажется информацией, предоставленной пользователем, и включая ее в текст SQL, а не предоставляя ее через переменные связывания. Использование переменных связывания не требуется, это просто один удобный подход для предотвращения внедрения SQL-кода. Есть и другие способы решения этой проблемы:
план оптимизатора с использованием сканирования индекса, а не поиска индекса; возможная потребность в выражении или функции для экранирования подстановочных знаков (возможный индекс в выражении или функции)
использование литеральных значений вместо переменных связывания влияет на масштабируемость
Заключение
Мне нравится подход Джоэла Спольски. Это умно. И это работает.
Но как только я это увидел, я сразу увидел потенциальную проблему с ним, и я не в силах позволить ему скользить. Я не хочу критиковать усилия других. Я знаю, что многие разработчики воспринимают свою работу очень лично, потому что они так много вкладывают в нее, и они так заботятся о ней. Поэтому, пожалуйста, поймите, это не личная атака. То, что я здесь определяю, это тип проблемы, которая возникает в процессе производства, а не тестирования.
Да, я далеко ушёл от первоначального вопроса. Но где еще оставить эту заметку о том, что я считаю важной проблемой с «избранным» ответом на вопрос?