Как сделать LIKE без учета регистра в базе данных с учетом регистра?

11

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

В базе данных, чувствительной к регистру, как бы вы написали, что она не учитывает регистр?

    Where Name like '%hospitalist%'
Джеймс
источник

Ответы:

17

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

-- Case sensitive example
SELECT *
FROM TABLE 
WHERE Name collate SQL_Latin1_General_CP1_CS_AS like '%hospitalist%'

-- Case insensitive example
SELECT *
FROM TABLE 
WHERE Name collate SQL_Latin1_General_CP1_CI_AS like '%hospitalist%'

Просто помните о проблемах производительности, которые могут возникнуть. Вам нужно будет сканировать кластерный индекс, чтобы настроить / найти значения при выполнении сопоставления. То, как вы пишете LIKEкусок, также делает запрос несортируемым.

Я подобрал уловку сличения из классов семинара SELECT Кендры Литтл . Вы можете найти дополнительную информацию о сопоставлении от Ben Snaidero из MS SQL Tips.

MSDN на Collate.

Shaulinator
источник
@stom Есть два метода. Либо а) Перенесите вопросы производительности на время обработки, а не selectвремя. Вы можете сделать это, создав новый столбец с преобразованным подмножеством данных, а затем индексировать его, как правило, в периоды, когда вы запускаете ETL. Это будет стоить содержание и не очень хороший метод. Б) Вы можете сделать поиск по запросу спорным или раздражительным. Изменение запроса, чтобы быть SELECT * FROM TABLE WHERE VALUE LIKE %hospitalistили SELECT * FROM TABLE WHERE VALUE LIKE hospitalist%будет работать. Кроме того, вы смотрите на оборудование или функции, чтобы увеличить скорость при плохом дизайне.
Шаулинатор
14

Хотя вы можете использовать скалярную функцию, такую ​​как UPPER или LOWER, и вы можете повторно сопоставить столбец, чтобы он больше не учитывал регистр, все эти подходы требуют преобразования данных в базовые данные, которые никогда не позволят выполнить поиск по индексу. Вы также ведете свой LIKE с помощью подстановочного знака, так что в любом случае это не так важно для вас в этом сценарии, но если вы когда-нибудь хотели найти эффективный способ поиска левой части строки И включить оптимизатор для поиска по индексу вы можете указать строку в скобках ([]) следующим образом:

SELECT *
FROM TABLE 
WHERE Name LIKE '[hH][oO][sS][pP][iI][tT][aA][lL][iI][sS][tT]%'

Этот пример ( ссылка на dbfiddle здесь ) лучше показывает, что я имею в виду:

CREATE TABLE #tmp_cohellation_fun
(
        ID  INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
    ,   myValue VARCHAR(50) COLLATE SQL_Latin1_General_CP1_CS_AS
)

-- Garbage values to represent data you don't want
INSERT INTO #tmp_cohellation_fun
SELECT  CAST(NEWID() AS VARCHAR(50))
FROM master.sys.configurations t1
    CROSS JOIN master.sys.configurations t2
    CROSS JOIN master.sys.configurations t3;

-- Sprinkle a little bit of good data
INSERT INTO #tmp_cohellation_fun
        (myValue)
VALUES  ('Apple')
    ,   ('apple')

-- Another healthy helping of garbage that we don't care about
INSERT INTO #tmp_cohellation_fun
SELECT  CAST(NEWID() AS VARCHAR(50))
FROM master.sys.configurations t1
    CROSS JOIN master.sys.configurations t2
    CROSS JOIN master.sys.configurations t3;

-- Some more good data
INSERT INTO #tmp_cohellation_fun
        (myValue)
VALUES
        ('aPple')
    ,   ('APPLE')
    ,   ('APple')


-- Final insert of garbage that we don't care about
INSERT INTO #tmp_cohellation_fun
SELECT  CAST(NEWID() AS VARCHAR(50))
FROM master.sys.configurations t1
    CROSS JOIN master.sys.configurations t2
    CROSS JOIN master.sys.configurations t3
;

-- Create a nonclustered rowstore index
CREATE INDEX ix_myValue ON #tmp_cohellation_fun (myValue)
;

SET STATISTICS XML ON
;

-- Seek, but incorrect results
SELECT  *
FROM    #tmp_cohellation_fun
WHERE   myValue LIKE 'apple%'
;

-- Scan, with correct results
SELECT  *
FROM    #tmp_cohellation_fun
WHERE   myValue COLLATE SQL_Latin1_General_CP1_CI_AS LIKE 'apple%'
;

-- Seek, with correct results
SELECT  *
FROM    #tmp_cohellation_fun
WHERE   myValue LIKE '[aA][pP][pP][lL][eE]%'
;

SET STATISTICS XML OFF
;

DROP TABLE IF EXISTS #tmp_cohellation_fun
Джон Айсбренер
источник
Любить это. Это вне меня, почему SQL не может просто изящно отступить, как это, когда вы говорите, что сортировка от чувствительного к регистру до нечувствительного к регистру, когда у вас есть два одинаковых в противном случае сопоставления. Я понимаю, почему ты не можешь пойти другим путем. Во всяком случае, это хорошая вещь.
Джон Лейдгрен
13

И это, и COLLATEответ будут влиять на производительность, поскольку они делают запрос не SARGable , но самый простой способ сделать это (как предложил Эдгар в комментарии):

WHERE LOWER(Name) LIKE '%hospitalist%' 

или

WHERE UPPER(Name) LIKE '%HOSPITALIST%' 
BradC
источник