Malheureusement, en SQL, je dois souvent utiliser des conditions "LIKE
" en raison de bases de données qui violent presque toutes les règles de normalisation. Je ne peux pas changer ça maintenant. Mais ce n'est pas pertinent à la question.
De plus, j’utilise souvent des conditions telles que WHERE something in (1,1,2,3,5,8,13,21)
pour une meilleure lisibilité et flexibilité de mes instructions SQL.
Est-il possible de combiner ces deux choses sans écrire des sous-sélections compliquées?
Je veux quelque chose d'aussi simple que WHERE something LIKE ('bla%', '%foo%', 'batz%')
au lieu de ceci:
WHERE something LIKE 'bla%'
OR something LIKE '%foo%'
OR something LIKE 'batz%'
Je travaille avec SQl Server et Oracle ici, mais je suis intéressé si cela est possible dans n'importe quel SGBDR.
Il n'y a pas de combinaison de LIKE & IN dans SQL, encore moins dans TSQL (SQL Server) ou PLSQL (Oracle). Cela est en partie dû au fait que la recherche en texte intégral est la solution recommandée.
Les implémentations Oracle et SQL Server FTS prennent en charge le mot clé CONTAINS, mais la syntaxe est toujours légèrement différente:
WHERE CONTAINS(t.something, 'bla OR foo OR batz', 1) > 0
WHERE CONTAINS(t.something, '"bla*" OR "foo*" OR "batz*"')
Référence:
Si vous voulez que votre déclaration soit facilement lisible, vous pouvez utiliser REGEXP_LIKE (disponible à partir d'Oracle version 10).
Un exemple de tableau:
SQL> create table mytable (something)
2 as
3 select 'blabla' from dual union all
4 select 'notbla' from dual union all
5 select 'ofooof' from dual union all
6 select 'ofofof' from dual union all
7 select 'batzzz' from dual
8 /
Table created.
La syntaxe originale:
SQL> select something
2 from mytable
3 where something like 'bla%'
4 or something like '%foo%'
5 or something like 'batz%'
6 /
SOMETH
------
blabla
ofooof
batzzz
3 rows selected.
Et une requête simple avec REGEXP_LIKE
SQL> select something
2 from mytable
3 where regexp_like (something,'^bla|foo|^batz')
4 /
SOMETH
------
blabla
ofooof
batzzz
3 rows selected.
MAIS ...
Je ne le recommanderais pas moi-même en raison de la performance médiocre. Je resterais avec plusieurs prédicats LIKE. Donc, les exemples étaient juste pour le plaisir.
vous êtes coincé avec le
WHERE something LIKE 'bla%'
OR something LIKE '%foo%'
OR something LIKE 'batz%'
sauf si vous remplissez une table temporaire (incluez les caractères génériques dans les données) et rejoignez comme ceci
FROM YourTable y
INNER JOIN YourTempTable t On y.something LIKE t.something
essayez-le (en utilisant la syntaxe SQL Server):
declare @x table (x varchar(10))
declare @y table (y varchar(10))
insert @x values ('abcdefg')
insert @x values ('abc')
insert @x values ('mnop')
insert @y values ('%abc%')
insert @y values ('%b%')
select distinct *
FROM @x x
WHERE x.x LIKE '%abc%'
or x.x LIKE '%b%'
select distinct x.*
FROM @x x
INNER JOIN @y y On x.x LIKE y.y
SORTIE:
x
----------
abcdefg
abc
(2 row(s) affected)
x
----------
abc
abcdefg
(2 row(s) affected)
Avec PostgreSQL, il existe la forme ANY
ou ALL
:
WHERE col LIKE ANY( subselect )
ou
WHERE col LIKE ALL( subselect )
où la sous-sélection renvoie exactement une colonne de données.
Je suggérerais d'utiliser une fonction utilisateur TableValue si vous souhaitez encapsuler les techniques de jointure interne ou de table temporaire ci-dessus. Cela lui permettrait de lire un peu plus clairement.
Après avoir utilisé la fonction split définie à: http://www.logiclabz.com/sql-server/split-function-in-sql-server-to-break-comma-separated-strings-into-table.aspx
on peut écrire ce qui suit en se basant sur une table que j'ai créée et appelée "Fish" (int id, varchar (50) Name)
SELECT Fish.* from Fish
JOIN dbo.Split('%ass,%e%',',') as Splits
on Name like Splits.items //items is the name of the output column from the split function.
Les sorties
1 Basse 2 Brochet 7 Pêcheur 8 Doré
Une autre solution devrait fonctionner sur n’importe quel SGBDR:
WHERE EXISTS (SELECT 1
FROM (SELECT 'bla%' pattern FROM dual UNION ALL
SELECT '%foo%' FROM dual UNION ALL
SELECT 'batz%' FROM dual)
WHERE something LIKE pattern)
Une approche serait de stocker les conditions dans une table temporaire (ou une variable de table dans SQL Server) et de les rejoindre comme ceci:
SELECT t.SomeField
FROM YourTable t
JOIN #TempTableWithConditions c ON t.something LIKE c.ConditionValue
Utilisez plutôt une jointure interne:
SELECT ...
FROM SomeTable
JOIN
(SELECT 'bla%' AS Pattern
UNION ALL SELECT '%foo%'
UNION ALL SELECT 'batz%'
UNION ALL SELECT 'abc'
) AS Patterns
ON SomeTable.SomeColumn LIKE Patterns.Pattern
tu peux même essayer ça
Une fonction
CREATE FUNCTION [dbo].[fn_Split](@text varchar(8000), @delimiter varchar(20))
RETURNS @Strings TABLE
(
position int IDENTITY PRIMARY KEY,
value varchar(8000)
)
AS
BEGIN
DECLARE @index int
SET @index = -1
WHILE (LEN(@text) > 0)
BEGIN
SET @index = CHARINDEX(@delimiter , @text)
IF (@index = 0) AND (LEN(@text) > 0)
BEGIN
INSERT INTO @Strings VALUES (@text)
BREAK
END
IF (@index > 1)
BEGIN
INSERT INTO @Strings VALUES (LEFT(@text, @index - 1))
SET @text = RIGHT(@text, (LEN(@text) - @index))
END
ELSE
SET @text = RIGHT(@text, (LEN(@text) - @index))
END
RETURN
END
Question
select * from my_table inner join (select value from fn_split('ABC,MOP',','))
as split_table on my_table.column_name like '%'+split_table.value+'%';
J'ai une solution simple, qui fonctionne dans postgresql au moins, en utilisant like any
suivi de la liste des regex. Voici un exemple, cherchant à identifier certains antibiotiques dans une liste:
select *
from database.table
where lower(drug_name) like any ('{%cillin%,%cyclin%,%xacin%,%mycine%,%cephal%}')
Je me demandais aussi quelque chose comme ça. Je viens de tester en utilisant une combinaison de SUBSTRING
et IN
et c'est une solution efficace pour ce genre de problème. Essayez la requête ci-dessous:
Select * from TB_YOUR T1 Where SUBSTRING(T1.Something, 1,3) IN ('bla', 'foo', 'batz')
Dans Oracle , vous pouvez utiliser une collection de la manière suivante:
WHERE EXISTS (SELECT 1
FROM TABLE(ku$_vcnt('bla%', '%foo%', 'batz%'))
WHERE something LIKE column_value)
Ici, j'ai utilisé un type de collection prédéfini ku$_vcnt
, mais vous pouvez en déclarer votre propre comme ceci:
CREATE TYPE my_collection AS TABLE OF VARCHAR2(4000);
Pour SQL Server, vous pouvez recourir à SQL dynamique.
La plupart du temps, dans de telles situations, le paramètre de la clause IN est basé sur certaines données de la base de données.
L'exemple ci-dessous est un peu "forcé", mais cela peut correspondre à divers cas réels trouvés dans des bases de données héritées.
Supposons que vous ayez une table Personnes où les noms de personnes sont stockés dans un seul champ Nom de la personne comme Prénom + + '' + Nom. Vous devez sélectionner toutes les personnes dans une liste de prénoms, stockées dans le champ NameToSelect dans le tableau NamesToSelect , ainsi que certains critères supplémentaires (filtrés par sexe, date de naissance, etc.)
Vous pouvez le faire comme suit
-- @gender is nchar(1), @birthDate is date
declare
@sql nvarchar(MAX),
@subWhere nvarchar(MAX)
@params nvarchar(MAX)
-- prepare the where sub-clause to cover LIKE IN (...)
-- it will actually generate where clause PersonName Like 'param1%' or PersonName Like 'param2%' or ...
set @subWhere = STUFF(
(
SELECT ' OR PersonName like ''' + [NameToSelect] + '%'''
FROM [NamesToSelect] t FOR XML PATH('')
), 1, 4, '')
-- create the dynamic SQL
set @sql ='select
PersonName
,Gender
,BirstDate -- and other field here
from [Persons]
where
Gender = @gender
AND BirthDate = @birthDate
AND (' + @subWhere + ')'
set @params = ' @gender nchar(1),
@birthDate Date'
EXECUTE sp_executesql @sql, @params,
@gender,
@birthDate
_ {Je travaille avec SQl Server et Oracle ici mais je suis intéressé si cela est possible dans n'importe quel SGBDR.
Teradata supporte LIKE ALL/ANY
syntax:
ALL chaque chaîne de la liste.
ANY toute chaîne de la liste.┌──────────────────────────────┬────────────────────────────────────┐ │ THIS expression … │ IS equivalent to this expression … │ ├──────────────────────────────┼────────────────────────────────────┤ │ x LIKE ALL ('A%','%B','%C%') │ x LIKE 'A%' │ │ │ AND x LIKE '%B' │ │ │ AND x LIKE '%C%' │ │ │ │ │ x LIKE ANY ('A%','%B','%C%') │ x LIKE 'A%' │ │ │ OR x LIKE '%B' │ │ │ OR x LIKE '%C%' │ └──────────────────────────────┴────────────────────────────────────┘
J'ai peut-être une solution à cela, même si cela ne fonctionne que dans SQL Server 2008, à ma connaissance. J'ai découvert que vous pouvez utiliser le constructeur de lignes décrit dans https://stackoverflow.com/a/7285095/894974 pour joindre une table "fictive" à l'aide d'une clause like ..__ est, regardez:
SELECT [name]
,[userID]
,[name]
,[town]
,[email]
FROM usr
join (values ('hotmail'),('gmail'),('live')) as myTable(myColumn) on email like '%'+myTable.myColumn+'%'
Tous les utilisateurs avec une adresse e-mail similaire à celle fournie dans la liste en seront résultés . J'espère que cela servira à tout le monde. Le problème me dérangeait depuis un moment.
Cela fonctionne pour les valeurs séparées par des virgules
DECLARE @ARC_CHECKNUM VARCHAR(MAX)
SET @ARC_CHECKNUM = 'ABC,135,MED,ASFSDFSF,AXX'
SELECT ' AND (a.arc_checknum LIKE ''%' + REPLACE(@arc_checknum,',','%'' OR a.arc_checknum LIKE ''%') + '%'')''
Evalue à:
AND (a.arc_checknum LIKE '%ABC%' OR a.arc_checknum LIKE '%135%' OR a.arc_checknum LIKE '%MED%' OR a.arc_checknum LIKE '%ASFSDFSF%' OR a.arc_checknum LIKE '%AXX%')
Si vous souhaitez utiliser des index, vous devez omettre le premier caractère '%'
.
Dans Oracle RBDMS, vous pouvez obtenir ce comportement en utilisant REGEXP_LIKE function.
Le code suivant testera si la chaîne trois est présente dans l'expression de liste un | deux | trois | quatre | cinq (dans lequel le symbole de tuyau "_ |" signifie une opération logique OR).
SELECT 'Success !!!' result
FROM dual
WHERE REGEXP_LIKE('three', 'one|two|three|four|five');
RESULT
---------------------------------
Success !!!
1 row selected.
L'expression précédente est équivalente à:
three=one OR three=two OR three=three OR three=four OR three=five
Alors ça va réussir.
D'autre part, le test suivant échouera.
SELECT 'Success !!!' result
FROM dual
WHERE REGEXP_LIKE('ten', 'one|two|three|four|five');
no rows selected
Plusieurs fonctions liées aux expressions régulières (REGEXP_ *) sont disponibles dans Oracle depuis la version 10g. Si vous êtes un développeur Oracle et que vous êtes intéressé par ce sujet, cela devrait être un bon début Utilisation d'expressions régulières avec Oracle Database .
Si vous utilisez MySQL, la recherche en texte intégral est la plus proche:
Pas de réponse comme ça:
SELECT * FROM table WHERE something LIKE ('bla% %foo% batz%')
Dans Oracle, pas de problème.
Peut-être pensez-vous que la combinaison ressemble à ceci:
SELECT *
FROM table t INNER JOIN
(
SELECT * FROM (VALUES('bla'),('foo'),('batz')) AS list(col)
) l ON t.column LIKE '%'+l.Col+'%'
Si vous avez défini l'index de texte intégral pour votre table cible, vous pouvez utiliser cette alternative:
SELECT *
FROM table t
WHERE CONTAINS(t.column, '"bla*" OR "foo*" OR "batz*"')
Depuis 2016, SQL Server inclut une STRING_SPLIT
fonction . J'utilise SQL Server v17.4 et cela a fonctionné pour moi:
DECLARE @dashboard nvarchar(50)
SET @dashboard = 'P1%,P7%'
SELECT * from Project p
JOIN STRING_SPLIT(@dashboard, ',') AS sp ON p.ProjectNumber LIKE sp.value
Dans Teradata, vous pouvez utiliser LIKE ANY ('%ABC%','%PQR%','%XYZ%')
. Ci-dessous un exemple qui a produit les mêmes résultats pour moi
--===========
-- CHECK ONE
--===========
SELECT *
FROM Random_Table A
WHERE (Lower(A.TRAN_1_DSC) LIKE ('%american%express%centurion%bank%')
OR Lower(A.TRAN_1_DSC) LIKE ('%bofi%federal%bank%')
OR Lower(A.TRAN_1_DSC) LIKE ('%american%express%bank%fsb%'))
;
--===========
-- CHECK TWO
--===========
SELECT *
FROM Random_Table A
WHERE Lower(A.TRAN_1_DSC) LIKE ANY
('%american%express%centurion%bank%',
'%bofi%federal%bank%',
'%american%express%bank%fsb%')