SQL-сервер ігнорує регістр у виразі where


89

Як побудувати запит SQL (MS SQL Server), де речення "де" не враховує регістр?

SELECT * FROM myTable WHERE myField = 'sOmeVal'

Я хочу, щоб результати повернулись, ігноруючи справу

Відповіді:


137

У конфігурації бази даних 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. Сподіваюся, ви або хтось, хто, здається, прихильно відповідає моїй відповіді, розробив на благо кого-небудь, як я, хто шукає та знаходить відповіді, подібні до мого.
Danny

1
Це підтримали, оскільки це цілком раціональне пояснення. Складіть надто великі накладні витрати, а що, якщо у вашому рядку є символи, які сортування не розуміє? Латиниця 1 - це паршива схема кодування. Успіху в отриманні значущих результатів, якщо у вашому рядку є апостроф (як: О'Брайен).
eggmatters

2
Проголосував також. Я можу придумати безліч випадків, коли це було б корисно. Крім того, часто існує більше ніж один хороший спосіб щось зробити.
Inversus

1
Змінити регістр рядка для цілей порівняння, як правило, погано. У деяких мовах перетворення випадків не здійснюються в обидва кінці. тобто LOWER (x)! = LOWER (UPPER (x)).
Ceisc

15

Найпопулярніші 2 відповіді (від Адама Робінзона та Андрейса Кайнікова ) є певними, певними , правильними, оскільки вони виконують технічну роботу, але їх пояснення помилкові, і тому в багатьох випадках можуть ввести в оману. Наприклад, хоча SQL_Latin1_General_CP1_CI_ASсортування у багатьох випадках спрацьовує, не слід вважати, що це відповідне співвідношення, що не враховує регістр. Насправді, враховуючи, що OP працює в базі даних із урахуванням регістру (або, можливо, двійковим) порівнянням, ми знаємо, що OP не використовує сортування, яке є типовим для такої кількості установок (особливо будь-якої, встановленої на ОС використовуючи американську англійську як мову): SQL_Latin1_General_CP1_CI_AS. Звичайно, OP може бути використаний SQL_Latin1_General_CP1_CS_AS, але при роботі з нимVARCHARданих, важливо не міняти кодову сторінку, оскільки це може призвести до втрати даних, і це контролюється мовою / культурою порівняння (тобто Latin1_General проти французької проти івриту тощо). Будь ласка, дивіться пункт 9 нижче.

Інші чотири відповіді хибні в різному ступені.

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

  1. Не використовувати UPPER(). Це абсолютно непотрібна зайва робота. Використовуйте COLLATEречення. Порівняння рядків потрібно зробити в будь-якому випадку, але використовуючи UPPER()також потрібно перевірити, символ за символом, щоб перевірити, чи є відображення верхнього регістру, а потім змінити його. І робити це потрібно з обох сторін. Додавання COLLATEпросто спрямовує обробку на створення ключів сортування з використанням іншого набору правил, ніж це було за замовчуванням. Використання COLLATE, безумовно, є більш ефективним (або "продуктивним", якщо вам подобається це слово :), ніж використання UPPER(), як доведено у цьому тестовому сценарії (на PasteBin) .

    Є також проблема, яку зазначає @Ceisc у відповіді @ Danny:

    У деяких мовах перетворення випадків не здійснюється в обидва кінці. тобто LOWER (x)! = LOWER (UPPER (x)).

    Поширеним прикладом є турецька літера “İ”.

  2. Ні, сортування не є налаштуванням для всієї бази даних, принаймні не в цьому контексті. Існує порівняння за замовчуванням на рівні бази даних, і воно використовується як за замовчуванням для змінених та новостворених стовпців, які не вказують COLLATEречення (що, ймовірно, звідки походить ця поширена помилка), але це не впливає безпосередньо на запити, якщо ви не порівнюючи рядкові літерали та змінні з іншими рядковими літералами та змінними, або ви посилаєтесь на метадані на рівні бази даних.

  3. Ні, порівняння не відповідає запиту.

  4. Сортування здійснюється за предикатом (тобто чимось операндом) або виразом, а не за запитом. І це справедливо для всього запиту, а не лише для цього WHEREпункту. Це охоплює ПРИЄДНАННЯ, ГРУПУВАТИ, ЗАМОВИТИ, РОЗДІЛИТИ, тощо.

  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[_]%';
    

Для отримання додаткової інформації про порівняння, кодування тощо, будь-ласка, відвідайте: Інформація про зіставлення


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
Нині функція називається LOWER
Девід Броссард,

@DavidBrossard та David Hermanns, я не думаю, що це було коли-небудь LCASE()у SQL Server (принаймні не те, що я бачу). Я думаю, що ця відповідь стосується зовсім іншої СУБД. Будь ласка, перегляньте мою відповідь для роз’яснень щодо порівняння рядків.
Соломон Руцкі

4

Ви можете примусити чутливий регістр, перекинувши на такий варіант:

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, з точки зору того, про що йдеться у цьому питанні, це не є ані налаштуванням для бази даних, ані запитом. Будь ласка, перегляньте мою відповідь для деталей.
Соломон Руцкі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.