Первоначальный вопрос был «Как я могу параметризовать запрос ...»
Позвольте мне заявить прямо здесь, что это не ответ на первоначальный вопрос. Уже есть некоторые доказательства этого в других хороших ответах.
С учетом сказанного, пометьте этот ответ, опустите его, отметьте его как не ответ ... делайте все, что считаете правильным.
См. Ответ от Марка Брэкетта для предпочтительного ответа, который я (и 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 '\'
Так что это решает проблему с подстановочным знаком%. Почти.
Избежать побега
Мы понимаем, что наше решение создало еще одну проблему. Спасательный персонаж. Мы видим, что нам также нужно будет избегать любых случаев появления экранирующего персонажа. На этот раз мы используем! в качестве побега:
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, которые заинтересованы в взаимозаменяемости использования любого механизма базы данных.)
Базовые знания
Мы , возможно, специальные знания в области для столбца, (то есть, набор допустимых значений насильственных для столбца. Мы можем знать априорно , что значения , сохраненные в столбце никогда не будут содержать знак процента, подчеркивание, или кронштейну пары. В этом случае мы просто включаем быстрый комментарий, что эти случаи покрыты.
Значения, хранящиеся в столбце, могут допускать использование символов% или _, но ограничение может потребовать экранирования этих значений, возможно, с использованием определенного символа, так что эти значения LIKE сравнения "безопасны". Опять же, быстрый комментарий о допустимом наборе значений и, в частности, о том, какой символ используется в качестве escape-символа, и он соответствует подходу Джоэла Спольски.
Но, при отсутствии специальных знаний и гарантии, для нас важно, по крайней мере, рассмотреть возможность обработки этих неясных угловых случаев и рассмотреть, является ли поведение разумным и «в соответствии со спецификацией».
Другие вопросы перепросмотрены
Я полагаю, что другие уже в достаточной степени указали на некоторые из других широко обсуждаемых проблемных областей:
Внедрение SQL (взятие того, что представляется информацией, предоставленной пользователем, и включение ее в текст SQL, а не предоставление их через переменные связывания. Использование переменных связывания не требуется, это всего лишь один удобный подход, чтобы помешать внедрению SQL. Есть и другие способы борьбы с этим:
план оптимизатора с использованием сканирования индекса, а не поиска индекса; возможная потребность в выражении или функции для экранирования подстановочных знаков (возможный индекс в выражении или функции)
использование литеральных значений вместо переменных связывания влияет на масштабируемость
Вывод
Мне нравится подход Джоэла Спольски. Это умно. И это работает.
Но как только я это увидел, я сразу же увидел потенциальную проблему, и не в моем характере позволять этому скользить. Я не хочу критиковать усилия других. Я знаю, что многие разработчики относятся к своей работе очень лично, потому что они так много в нее вкладывают, и они так заботятся об этом. Поэтому, пожалуйста, поймите, это не личная атака. То, что я определяю здесь, это тип проблемы, которая возникает в процессе производства, а не тестирования.
Да, я далеко ушёл от первоначального вопроса. Но где еще оставить эту заметку о том, что я считаю важной проблемой с «отобранным» ответом на вопрос?