30

I know I can write a query that will return all rows that contain any number of values in a given column, like so:

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

but if val1, for example, can appear anywhere in my_col, which has datatype varchar(300), I might instead write:

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

Is there a way of combing these two techniques. I need to search for some 30 possible values that may appear anywhere in the free-form text of the column.

Combining these two statements in the following ways does not seem to work:

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

select * from tbl where my_col in ('%val1%', '%val2%', 'val3%',....)
Aldwoni
  • 1,168
  • 10
  • 24
DeveloperM
  • 1,129
  • 7
  • 17
  • 30

11 Answers11

50

What would be useful here would be a LIKE ANY predicate as is available in PostgreSQL

SELECT * 
FROM tbl
WHERE my_col LIKE ANY (ARRAY['%val1%', '%val2%', '%val3%', ...])

Unfortunately, that syntax is not available in Oracle. You can expand the quantified comparison predicate using OR, however:

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

Or alternatively, create a semi join using an EXISTS predicate and an auxiliary array data structure (see this question for details):

SELECT *
FROM tbl t
WHERE EXISTS (
  SELECT 1
  -- Alternatively, store those values in a temp table:
  FROM TABLE (sys.ora_mining_varchar2_nt('%val1%', '%val2%', '%val3%'/*, ...*/))
  WHERE t.my_col LIKE column_value
)

For true full-text search, you might want to look at Oracle Text: http://www.oracle.com/technetwork/database/enterprise-edition/index-098492.html

Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
  • As will any query using LIKE. It is best avoided. – GolezTrol May 20 '11 at 15:41
  • @Lukas: Thank you for responding, but the pool of values may grow quite large and I don't want to string out those wildcarded values forever. – DeveloperM May 20 '11 at 15:41
  • @DeveloperM: Essentially that's what the `EXISTS` operator does. You want to have a second table joined to the first one (i.e. get the `JOIN` effect), without actually joining it. Read on a bit on here: http://www.dbspecialists.com/files/presentations/semijoins.html – Lukas Eder May 20 '11 at 16:02
  • 1
    +1 for the Oracle Text reference. Any system that needs to rely on this kind of searching would likely be better served on built-in Oracle text search functionality rather than a cobbled list of LIKE operations. – DCookie May 20 '11 at 16:22
  • @Lukas: ok, I used SELECT * FROM RR WHERE EXISTS (SELECT 1 FROM Bad_conditions WHERE RR.ID = 'xyz' and upper(RR.DESCRIPTION_1) LIKE Bad_conditions.DESCRIPTION_1) This seems to do the trick. Thanks for the help! – DeveloperM May 20 '11 at 18:41
  • @LukasEder Nice, the syntax [LIKE ALL](https://stackoverflow.com/a/52264937/5070879) would be a great extension – Lukasz Szozda Jun 27 '19 at 15:11
  • The alternative approach to using semi-join was love. Thanks a lot. This was the first time I got introduced to - sys.ora_mining_varchar2_nt. – hagrawal7777 Oct 27 '22 at 13:10
12

A REGEXP_LIKE will do a case-insensitive regexp search.

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

This will be executed as a full table scan - just as the LIKE or solution, so the performance will be really bad if the table is not small. If it's not used often at all, it might be ok.

If you need some kind of performance, you will need Oracle Text (or some external indexer).

To get substring indexing with Oracle Text you will need a CONTEXT index. It's a bit involved as it's made for indexing large documents and text using a lot of smarts. If you have particular needs, such as substring searches in numbers and all words (including "the" "an" "a", spaces, etc) , you need to create custom lexers to remove some of the smart stuff...

If you insert a lot of data, Oracle Text will not make things faster, especially if you need the index to be updated within the transactions and not periodically.

KarlP
  • 5,149
  • 2
  • 28
  • 41
11

No, you cannot do this. The values in the IN clause must be exact matches. You could modify the select thusly:

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

If the val1, val2, val3... are similar enough, you might be able to use regular expressions in the REGEXP_LIKE operator.

DCookie
  • 42,630
  • 11
  • 83
  • 92
8

Yes, you can use this query (Instead of 'Specialist' and 'Developer', type any strings you want separated by comma and change employees table with your table)

SELECT * FROM employees em
WHERE EXISTS (select 1 from  table(sys.dbms_debug_vc2coll('Specialist', 'Developer')) mt  
              where em.job like ('%' || mt.column_value || '%'));

Why my query is better than the accepted answer: You don't need a CREATE TABLE permission to run it. This can be executed with just SELECT permissions.

iDevlop
  • 24,841
  • 11
  • 90
  • 149
GabrielBB
  • 2,479
  • 1
  • 35
  • 49
8

In Oracle you can use regexp_like as follows:

select *
from   table_name
where  regexp_like (name, '^(value-1|value-2|value-3....)');

The caret (^) operator to indicate a beginning-of-line character & The pipe (|) operator to indicate OR operation.

Imran
  • 171
  • 1
  • 5
4

This one is pretty fast :

select * from listofvalue l 
inner join tbl on tbl.mycol like '%' || l.value || '%'
2

Just to add on @Lukas Eder answer.

An improvement to avoid creating tables and inserting values (we could use select from dual and unpivot to achieve the same result "on the fly"):

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)
PKey
  • 3,715
  • 1
  • 14
  • 39
2

I prefer this

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

I'm not saying it's optimal but it works and it's easily understood. Most of my queries are adhoc used once so performance is generally not an issue for me.

AWOLKiwi
  • 19
  • 1
  • 3
1
select * from tbl
 where exists (select 1 from all_likes where all_likes.value = substr(tbl.my_col,0, length(tbl.my_col)))
Stefan Ferstl
  • 5,135
  • 3
  • 33
  • 41
Vishnu
  • 11
  • 1
  • Straightforward plagiarism of another answer in this thread. Obviously a "test" by a drive-by poster. – APC Mar 13 '18 at 08:14
1

You can put your values in ODCIVARCHAR2LIST and then join it as a regular table.

select tabl1.* FROM tabl1 LEFT JOIN 
(select column_value txt from table(sys.ODCIVARCHAR2LIST
('%val1%','%val2%','%val3%')
)) Vals ON tabl1.column LIKE Vals.txt WHERE Vals.txt IS NOT NULL
Razneesh
  • 1,147
  • 3
  • 13
  • 29
0

You don't need a collection type as mentioned in https://stackoverflow.com/a/6074261/802058. Just use an subquery:

SELECT *
FROM tbl t
WHERE EXISTS (
    SELECT 1
    FROM (
        SELECT 'val1%' AS val FROM dual
        UNION ALL
        SELECT 'val2%' AS val FROM dual
        -- ...
        -- or simply use an subquery here
    )
    WHERE t.my_col LIKE val
)
Toru
  • 905
  • 1
  • 9
  • 28
  • 1
    Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Mark Rotteveel Feb 11 '21 at 17:22