web-dev-qa-db-fra.com

Comment faire un LIKE insensible à la casse dans une base de données sensible à la casse?

Mon fournisseur exige que la base de données de l'entrepôt de données soit sensible à la casse, mais je dois lui faire des requêtes non sensibles à la casse.

Dans une base de données sensible à la casse, comment écririez-vous cela pour ne pas respecter la casse?

    Where Name like '%hospitalist%'
9
James

Vous pouvez ajouter un nouveau classement à votre requête de sélection pour trouver sensible à la casse ou insensible.

-- 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%'

Soyez juste conscient des problèmes de performances que cela pourrait présenter. Vous devrez analyser l'index cluster pour ajuster/trouver les valeurs lorsque vous effectuez le classement. La façon dont vous écrivez la pièce LIKE rend également la requête non-sargable.

J'ai choisi l'astuce de collation de Kendra Little's SELECT Classes de séminaire. Vous pouvez trouver des informations de classement supplémentaires sur Ben Snaidero de MS SQL Tips.

MSDN sur Collate.

15
Shaulinator

Pendant que vous pouvez utiliser une fonction scalaire telle que SUPÉRIEUR ou INFÉRIEUR et vous pouvez recalculer la colonne afin qu'elle ne soit plus sensible à la casse, ces approches tous nécessitent une conversion des données par rapport aux données de base qui ne permettront jamais une recherche d'index. Vous menez également votre LIKE avec un caractère générique, donc ce n'est pas autant une préoccupation pour vous dans ce scénario de toute façon, mais si vous avez toujours voulu rechercher la partie gauche d'une chaîne de manière efficace [~ # ~] et [~ # ~] permettent à l'optimiseur de rechercher à travers un index, vous pouvez spécifier votre chaîne avec des crochets ([]) comme suit:

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

Cet exemple ( lien dbfiddle ici ) montre mieux ce que je veux dire:

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
13
John Eisbrener

Ceci et la réponse COLLATE auront un impact sur les performances, grâce à eux rendant la requête non SARGable , mais la façon la plus simple de le faire (comme Edgar suggéré dans un commentaire) est:

WHERE LOWER(Name) LIKE '%hospitalist%' 

ou

WHERE UPPER(Name) LIKE '%HOSPITALIST%' 
11
BradC