web-dev-qa-db-fra.com

Utilisation de LIKE dans une clause Oracle IN

Je sais que je peux écrire une requête qui renvoie toutes les lignes contenant un nombre quelconque de valeurs dans une colonne donnée, comme ceci:

Select * from tbl where my_col in (val1, val2, val3,... valn)

mais si val1, par exemple, peut apparaître n'importe où dans my_col, qui a le type de données varchar (300), je pourrais plutôt écrire:

select * from tbl where my_col LIKE '%val1%'

Est-il possible de combiner ces deux techniques? J'ai besoin de rechercher une trentaine de valeurs possibles qui peuvent apparaître n'importe où dans le texte libre de la colonne. 

La combinaison de ces deux déclarations de la manière suivante ne semble pas fonctionner:

select * from tbl where my_col LIKE ('%val1%', '%val2%', 'val3%',....) 

select * from tbl where my_col in ('%val1%', '%val2%', 'val3%',....)
14
DeveloperM
select * from tbl 
where my_col like '%val1%' or my_col like'%val2%' or my_col like '%val3%', ...

Mais méfiez-vous, cela pourrait être assez lent ... Vous pouvez également insérer toutes les valeurs acceptables (y compris les signes %) dans une table et les joindre à demi:

select * from tbl
where exists (select 1 from all_likes where tbl.my_col like all_likes.value)

Pour une véritable recherche en texte intégral, vous pouvez consulter Oracle Text:

http://www.Oracle.com/technetwork/database/enterprise-edition/index-098492.html

22
Lukas Eder

Non, vous ne pouvez pas faire ça. Les valeurs de la clause IN doivent correspondre exactement. Vous pouvez modifier le selectly ainsi:

SELECT *
  FROM tbl
 WHERE my_col LIKE %val1%
    OR my_col LIKE %val2%
    OR my_col LIKE %val3%
 ...

Si les valeurs val1, val2, val3 ... sont suffisamment similaires, vous pourrez peut-être utiliser des expressions régulières dans l'opérateur REGEXP_LIKE.

6
DCookie

Un REGEXP_LIKE fera une recherche d'expression rationnelle insensible à la casse.

select * from Users where Regexp_Like (User_Name, 'karl|anders|leif','i')

Ceci sera exécuté comme une analyse de table complète - tout comme la solution LIKE or, ainsi la performance sera vraiment mauvaise si la table n'est pas petite. Si ce n'est pas souvent utilisé, ça pourrait aller.

Si vous avez besoin de performances, vous aurez besoin de Oracle Text (ou d'un indexeur externe). 

Pour obtenir l'indexation de sous-chaînes avec Oracle Text, vous aurez besoin d'un index CONTEXT. C'est un peu compliqué car il est conçu pour indexer de gros documents et du texte en utilisant beaucoup d'intelligence. Si vous avez des besoins particuliers, tels que la recherche de sous-chaînes dans les nombres et tous les mots (y compris "le" "", les espaces, etc.), vous devez créer des lexeurs personnalisés pour supprimer certains éléments malins ...

Si vous insérez beaucoup de données, Oracle Text n'accélérera pas les choses, surtout si vous avez besoin que l'index soit mis à jour au sein des transactions et non périodiquement.

2
KarlP

Juste pour ajouter sur la réponse de @Lukas Eder.

Une amélioration pour éviter de créer des tables et d'insérer des valeurs .__ (nous pourrions utiliser select from dual et unpivot pour obtenir le même résultat "à la volée"): 

with all_likes as  
(select * from 
    (select '%val1%' like_1, '%val2%' like_2, '%val3%' like_3, '%val4%' as like_4, '%val5%' as like_5 from dual)
    unpivot (
     united_columns for subquery_column in ("LIKE_1", "LIKE_2", "LIKE_3", "LIKE_4", "LIKE_5"))
  )
    select * from tbl
    where exists (select 1 from all_likes where tbl.my_col like all_likes.united_columns)
1
Plirkee

Oui, vous pouvez utiliser cette requête:

  SELECT * FROM RandomTable rt 
    WHERE EXISTS (select 1 from table(sys.dbms_debug_vc2coll(//INSERT STRINGS SEPARATED BY COMMA HERE)) as MemoryTable mt where rt.name like mt.column_value);

Pourquoi ma requête est meilleure que la réponse acceptée: vous n'avez pas besoin d'une autorisation CREATE TABLE pour l'exécuter. Cela peut être exécuté avec uniquement les autorisations SELECT.

1
GabrielBB

Je préfère ça

WHERE CASE WHEN my_col LIKE '%val1%' THEN 1    
           WHEN my_col LIKE '%val2%' THEN 1
           WHEN my_col LIKE '%val3%' THEN 1
           ELSE 0
           END = 1

Je ne dis pas que c'est optimal mais ça marche et c'est facile à comprendre. La plupart de mes requêtes sont utilisées une seule fois par ad hoc, donc les performances ne sont généralement pas un problème pour moi.

1
AWOLKiwi

Celui-ci est assez rapide:

select * from listofvalue l 
inner join tbl on tbl.mycol like '%' || l.value || '%'
1
select * from tbl
 where exists (select 1 from all_likes where all_likes.value = substr(tbl.my_col,0, length(tbl.my_col)))
0
Vishnu