SQL-сервер игнорирует регистр в выражении where

88

Как создать SQL-запрос (MS SQL Server), в котором в предложении «where» регистр не учитывается?

SELECT * FROM myTable WHERE myField = 'sOmeVal'

Я хочу, чтобы результаты вернулись, не обращая внимания на дело

Рауль Аграйт
источник

Ответы:

136

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

SELECT * FROM myTable WHERE myField = 'sOmeVal' COLLATE SQL_Latin1_General_CP1_CI_AS

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

Адам Робинсон
источник
Просто для подтверждения, это нужно добавить только один раз, в конце WHEREоператора, и это повлияет на все WHEREпредложения, верно?
ashleedawg
Хотите узнать, есть ли у вашего ответа какие-либо проблемы с производительностью, если преобразовать значение столбца в UPPERили LOWERрегистр, а затем использовать LIKEдля поиска?
Shaijut
1
@ashleedawg - хороший вопрос .. похоже, это построчная настройка.
Лео Гурдиан
29

Обычно при сравнении строк регистр не учитывается. Если ваша база данных настроена на сортировку с учетом регистра, вам необходимо принудительно использовать регистронезависимую:

SELECT balance FROM people WHERE email = 'billg@microsoft.com'
  COLLATE SQL_Latin1_General_CP1_CI_AS 
Андрей Каиников
источник
@AskeB. и Андрейс: Технически это не проблема конфигурации базы данных. Пожалуйста, посмотрите мой ответ, чтобы прояснить сравнение строк.
Соломон Рутцки
21

Я нашел другое решение в другом месте; то есть использовать

upper(@yourString)

но все здесь говорят, что в SQL Server это не имеет значения, потому что он все равно игнорирует регистр? Я почти уверен, что наша база данных чувствительна к регистру.

Дэнни
источник
7
Вы правы, что базу данных можно сделать чувствительной к регистру, но это довольно неэффективно, даже если это необходимо. COLLATE - ключевое слово для использования.
mjaggard
1
Спасибо, что подняли этот вопрос, @mjaggard. Я надеюсь, что вы или любой, кто, кажется, отрицает мой ответ, доработаете для блага всех, кто, как я, ищет и находит такие ответы, как мой.
Дэнни
1
Проголосовал за это, так как это совершенно рациональное объяснение. Сортировка привносит слишком много накладных расходов, а что, если в вашей строке есть символы, которые не распознаются сопоставлением? Latin 1 - паршивая схема кодирования. Удачи в получении значимых результатов, если в вашей строке есть апостроф (например, O'Brien).
eggmatters
2
Проголосовали тоже. Я могу придумать множество случаев, когда это было бы полезно. Кроме того, часто есть несколько хороших способов что-то сделать.
Inversus
1
Менять регистр строки для сравнения, как правило, плохо. В некоторых языках преобразование не происходит туда и обратно. т.е. НИЖНИЙ (x)! = НИЖНИЙ (ВЕРХНИЙ (x)).
Ceisc
15

Два основных ответа (от Адама Робинсона и Андрея Кайникова ) в некотором роде верны в том смысле, что они технически работают, но их объяснения неверны и поэтому во многих случаях могут вводить в заблуждение. Например, хотя SQL_Latin1_General_CP1_CI_ASсопоставление будет работать во многих случаях, не следует предполагать, что оно является подходящим сопоставлением без учета регистра. Фактически, учитывая, что OP работает в базе данных с чувствительным к регистру (или, возможно, двоичным) сопоставлением, мы знаем, что OP не использует сопоставление, которое является значением по умолчанию для многих установок (особенно тех, которые установлены в ОС. используя американский английский как язык): SQL_Latin1_General_CP1_CI_AS. Конечно, OP можно использовать SQL_Latin1_General_CP1_CS_AS, но при работе сVARCHARdata, важно не изменять кодовую страницу, так как это может привести к потере данных, и это контролируется локалью / культурой сопоставления (например, Latin1_General vs French vs Hebrew и т. д.). См. Пункт 9 ниже.

Остальные четыре ответа в той или иной степени неверны.

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

  1. Не использовать UPPER(). Это совершенно ненужная дополнительная работа. Используйте COLLATEпредложение. В любом случае необходимо выполнить сравнение строк, но при использовании UPPER()также необходимо проверять символ за символом, чтобы увидеть, есть ли отображение в верхнем регистре, а затем изменить его. Причем делать это нужно с обеих сторон. Добавление COLLATEпросто направляет обработку для создания ключей сортировки с использованием набора правил, отличного от того, который использовался по умолчанию. Использование COLLATEопределенно более эффективно (или «производительно», если вам нравится это слово :), чем использование UPPER(), как показано в этом тестовом сценарии (на PasteBin) .

    Существует также проблема, отмеченная @Ceisc в ответе @Danny:

    В некоторых языках преобразование не происходит туда и обратно. т.е. НИЖНИЙ (x)! = НИЖНИЙ (ВЕРХНИЙ (x)).

    Турецкая заглавная буква «İ» является типичным примером.

  2. Нет, сортировка не является параметром всей базы данных, по крайней мере, в этом контексте. Существует сопоставление по умолчанию на уровне базы данных, и оно используется по умолчанию для измененных и вновь созданных столбцов, в которых не указано COLLATEусловие (что, вероятно, является источником этого распространенного заблуждения), но оно не влияет на запросы напрямую, если вы не сравнение строковых литералов и переменных с другими строковыми литералами и переменными, или вы ссылаетесь на метаданные уровня базы данных.

  3. Нет, сортировка выполняется не по запросу.

  4. Сопоставления выполняются для каждого предиката (т.е. чего-то операнда чего-то) или выражения, а не запроса. И это верно для всего запроса, а не только для WHEREпредложения. Это касается JOINs, GROUP BY, ORDER BY, PARTITION BY и т. Д.

  5. Нет, не конвертируйте в VARBINARY(например convert(varbinary, myField) = convert(varbinary, 'sOmeVal')) по следующим причинам:

    1. это двоичное сравнение, которое не чувствительно к регистру (это то, о чем спрашивает этот вопрос)
    2. если вам нужно двоичное сравнение, используйте двоичное сопоставление. Используйте тот, который заканчивается на, _BIN2если вы используете SQL Server 2008 или новее, иначе у вас нет выбора, кроме как использовать тот, который заканчивается на _BIN. Если данные есть, NVARCHARто не имеет значения, какой язык вы используете, поскольку в этом случае они все одинаковы, следовательно, Latin1_General_100_BIN2всегда работает. Если данные VARCHAR, вы должны использовать один и тот же локаль , что данные в настоящее время (например Latin1_General, French, Japanese_XJISи т.д.) , так как локаль определяет кодовую страницу, которая используется, и изменения кода страницы могут изменять данные (т.е. потери данных).
    3. использование типа данных переменной длины без указания размера будет зависеть от размера по умолчанию, и есть два разных значения по умолчанию в зависимости от контекста, в котором используется тип данных. Для строковых типов это либо 1, либо 30. При использовании с CONVERT()ним будет использоваться значение по умолчанию 30. Опасность заключается в том, что если длина строки может превышать 30 байт, она будет автоматически усечена, и вы, вероятно, получите неверные результаты из этого предиката.
    4. Даже если вам нужно сравнение с учетом регистра, двоичные сопоставления не чувствительны к регистру (еще одно очень распространенное заблуждение).
  6. Нет, LIKEне всегда с учетом регистра. Он использует сопоставление столбца, на который указывает ссылка, или сопоставление базы данных, если переменная сравнивается со строковым литералом, или сопоставление, указанное с помощью необязательного COLLATEпредложения.

  7. LCASEне является функцией SQL Server. Похоже, это либо Oracle, либо MySQL. Или, возможно, Visual Basic?

  8. Поскольку контекст вопроса - сравнение столбца со строковым литералом, ни сортировка экземпляра (часто называемая «сервером»), ни сортировка базы данных не имеют здесь прямого влияния. Параметры сортировки хранятся для каждого столбца, и каждый столбец может иметь разные параметры сортировки, и эти параметры сортировки не обязательно должны совпадать с параметрами сортировки по умолчанию в базе данных или параметрами сортировки экземпляра. Конечно, сортировка экземпляра - это значение по умолчанию для того, что вновь созданная база данных будет использовать в качестве сопоставления по умолчанию, если COLLATEпредложение не было указано при создании базы данных. Точно так же параметры сортировки по умолчанию для базы данных - это то, что будет использовать измененный или вновь созданный столбец, если COLLATEпредложение не было указано.

  9. Следует использовать параметры сортировки без учета регистра, которые в остальном аналогичны параметрам сортировки столбца. Используйте следующий запрос, чтобы найти параметры сортировки столбца (измените имя таблицы и имя схемы):

    SELECT col.*
    FROM   sys.columns col
    WHERE  col.[object_id] = OBJECT_ID(N'dbo.TableName')
    AND    col.[collation_name] IS NOT NULL;
    

    Тогда просто измените значение _CSна быть _CI. Итак, Latin1_General_100_CS_ASстал бы Latin1_General_100_CI_AS.

    Если в столбце используется двоичная сортировка (оканчивающаяся на _BINили _BIN2), найдите аналогичную сортировку, используя следующий запрос:

    SELECT *
    FROM   sys.fn_helpcollations() col
    WHERE  col.[name] LIKE N'{CurrentCollationMinus"_BIN"}[_]CI[_]%';
    

    Например, предполагая, что столбец используется Japanese_XJIS_100_BIN2, сделайте следующее:

    SELECT *
    FROM   sys.fn_helpcollations() col
    WHERE  col.[name] LIKE N'Japanese_XJIS_100[_]CI[_]%';
    

Для получения дополнительной информации о параметрах сортировки, кодировки, и т.д., пожалуйста , посетите: Collations информацию

Соломон Руцки
источник
7

Нет, только с помощью LIKEне получится. LIKEищет значения, точно соответствующие вашему заданному шаблону. В этом случае LIKEбудет найден только текст «sOmeVal», а не «someval».

Практическое решение - использовать LCASE()функцию. LCASE('sOmeVal')получает строчную строку вашего текста: 'someval'. Если вы используете эту функцию для обеих сторон своего сравнения, она работает:

SELECT * FROM myTable WHERE LCASE(myField) LIKE LCASE('sOmeVal')

Этот оператор сравнивает две строчные строки, так что ваш 'sOmeVal' будет соответствовать всем другим обозначениям 'someval' (например, 'Someval', 'sOMEVAl' и т. Д.).

Дэвид Херманнс
источник
7
В 99,9% установок SQL Server, которые сопоставляют _CI, LIKE нечувствителен к регистру.
RichardTheKiwi
1
В настоящее время функция называется НИЖНИЙ
Дэвид Броссар
@DavidBrossard и David Hermanns, я не думаю, что это когда-либо было LCASE()в SQL Server (по крайней мере, я не вижу). Я думаю, что это ответ для совершенно другой СУБД. Пожалуйста, посмотрите мой ответ, чтобы прояснить сравнение строк.
Соломон Руцки
4

Вы можете принудительно использовать регистр, приведя к такому типу varbinary:

SELECT * FROM myTable 
WHERE convert(varbinary, myField) = convert(varbinary, 'sOmeVal')

источник
3
Хотя это функционально, это не рекомендуется. Сопоставления предназначены для управления сортировкой и сравнением строк.
Адам Робинсон,
@AdamRobinson, разве это не о «сравнении строк»?
Fandango68
@ Fandango68 Да, это так, и Адам говорит, что сопоставления лучше при сравнении строк.
JLRishe
@ Fandango68 Этот ответ неверен на нескольких уровнях. Подробности смотрите в моем ответе , особенно в пункте 5.
Соломон Рутцки
@AdamRobinson Пожалуйста, прочтите мой ответ, чтобы прояснить сравнение строк.
Соломон Рутцки
2

В какой базе данных вы находитесь? В MS SQL Server это параметр для всей базы данных, или вы можете переопределить его для каждого запроса с помощью ключевого слова COLLATE.

Чейз Зайберт
источник
Всем привет. Для SQL Server, с точки зрения того, о чем идет речь, это не настройка всей базы данных и не для каждого запроса. См. Подробности в моем ответе .
Соломон Рутцки