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


11

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

Réponses:


17

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 LIKEpièce rend également la requête non discutable.

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

MSDN sur Collate.


@stom Il existe deux méthodes. Soit a) Déplacez les problèmes de performances vers le temps de traitement et non vers le selecttemps. Pour ce faire, créez une nouvelle colonne avec un sous-ensemble des données transformées, puis indexez-la, généralement pendant les périodes où vous exécutez ETL. Cela aurait un coût d'entretien et n'est pas une excellente méthode. B) Vous pouvez rendre la recherche de requête défendable ou discutable. Changer la requête pour qu'elle soit SELECT * FROM TABLE WHERE VALUE LIKE %hospitalistou SELECT * FROM TABLE WHERE VALUE LIKE hospitalist%fonctionnerait. En dehors de cela, vous recherchez du matériel ou des fonctionnalités pour augmenter la vitesse de mauvaise conception.
Shaulinator

14

Bien que vous puissiez utiliser une fonction scalaire telle que UPPER ou LOWER et que vous pouvez recalculer la colonne afin qu'elle ne soit plus sensible à la casse, ces approches nécessitent toutes une conversion des données par rapport aux données de base qui ne permettront jamais une recherche d'index. Vous dirigez é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 autoriser l'optimiseur pour rechercher dans 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

Aimer. Je ne comprends pas pourquoi SQL ne pouvait pas simplement se replier avec élégance comme ceci lorsque vous dites assembler de la casse à l'insensible à la casse, lorsque vous avez deux classements par ailleurs identiques. Je comprends pourquoi vous ne pouvez pas aller dans l'autre sens. Quoi qu'il en soit, ce sont de bonnes choses.
John Leidegren

13

Cela et la COLLATEréponse auront un impact sur les performances, car ils rendront la requête non SARGable , mais la façon la plus simple de le faire (comme Edgar l'a suggéré dans un commentaire) est:

WHERE LOWER(Name) LIKE '%hospitalist%' 

ou

WHERE UPPER(Name) LIKE '%HOSPITALIST%' 
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.