web-dev-qa-db-ja.com

SQLに "LIKE"と "IN"の組み合わせはありますか?

SQL Iでは(残念ながら)データベースが正規化のほぼすべての規則に違反するため、 "LIKE"条件を使用する必要があります。今は変更できません。しかし、それは質問とは無関係です。

さらに、SQLステートメントの読みやすさと柔軟性を高めるために、WHERE something in (1,1,2,3,5,8,13,21)のような条件をよく使用します。

複雑な副選択を書かずにこれら2つのことを組み合わせることが可能な方法はありますか?

私はこれの代わりにWHERE something LIKE ('bla%', '%foo%', 'batz%')と同じくらい簡単なものが欲しいです:

WHERE something LIKE 'bla%'
OR something LIKE '%foo%'
OR something LIKE 'batz%'

私はここでSQl ServerとOracleを使っていますが、これがどのRDBMSでも可能であるかどうかに興味があります。

290
selfawaresoup

SQLにはLIKE&INの組み合わせはありませんが、TSQL(SQL Server)またはPLSQL(Oracle)にはそれほどありません。その理由の一部は、全文検索(FTS)が推奨される代替手段であるためです。

OracleとSQL Serverの両方のFTS実装でCONTAINSキーワードがサポートされていますが、構文は若干異なります。

Oracle:

WHERE CONTAINS(t.something, 'bla OR foo OR batz', 1) > 0

SQLサーバー:

WHERE CONTAINS(t.something, '"bla*" OR "foo*" OR "batz*"')

参照:

174
OMG Ponies

文を読みやすくするには、REGEXP_LIKE(Oracleバージョン10以降で使用可能)を使用します。

テーブルの例:

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.

元の構文

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.

REGEXP_LIKEを使用した簡単なクエリ

SQL> select something
  2    from mytable
  3   where regexp_like (something,'^bla|foo|^batz')
  4  /

SOMETH
------
blabla
ofooof
batzzz

3 rows selected.

しかし... ...

性能があまり良くないので、私は自分ではお勧めしません。私はいくつかのLIKE述語を使い続けます。そのため、例は面白かったです。

55
Rob van Wijk

あなたは立ち往生している

WHERE something LIKE 'bla%'
OR something LIKE '%foo%'
OR something LIKE 'batz%'

あなたが(データと一緒にワイルドカードを含める)一時テーブルを作成し、このように参加しない限り:

FROM YourTable                y
    INNER JOIN YourTempTable  t On y.something LIKE t.something

試してみる(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

出力:

x
----------
abcdefg
abc

(2 row(s) affected)

x
----------
abc
abcdefg

(2 row(s) affected)
43
KM.

PostgreSQLには ANYまたはALLの形式があります。

WHERE col LIKE ANY( subselect )

または

WHERE col LIKE ALL( subselect )

副選択が正確に1列のデータを戻すところ。

18
Benoit

別の解決策は、あらゆるRDBMSで動作するはずです。

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)
12
mik

上記の内部結合または一時テーブルの手法をカプセル化したい場合は、TableValueユーザー関数を使用することをお勧めします。これにより、もう少し明確に読むことができます。

で定義された分割関数を使用した後: http://www.logiclabz.com/sql-server/split-function-in-sql-server-to-break-comma-separated-strings-into-table.aspx

作成した "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.

アウトプット

[ベース] 1ベース[2]パイク[7] 7アングラー[8] 8ウォールアイ[0]
10
Famous Nerd

1つの方法は、条件を一時テーブル(またはSQL Serverのテーブル変数)に格納して、次のように結合することです。

SELECT t.SomeField
FROM YourTable t
   JOIN #TempTableWithConditions c ON t.something LIKE c.ConditionValue
7
AdaTheDev

代わりに内部結合を使用してください。

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
7
A-K

あなたもこれを試すことができます

関数

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

問い合わせ

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

私は postgresql で動く簡単な解決法を持っています、少なくともlike anyの後に正規表現のリストを使います。リストの中でいくつかの抗生物質を識別することを見て、例はここにあります:

select *
from database.table
where lower(drug_name) like any ('{%cillin%,%cyclin%,%xacin%,%mycine%,%cephal%}')
3
mkomo

私はそのようなことについても疑問に思いました。私はSUBSTRINGINを組み合わせてテストしたところで、これはこの種の問題に対する効果的な解決策です。以下のクエリを試してください。

Select * from TB_YOUR T1 Where SUBSTRING(T1.Something, 1,3) IN ('bla', 'foo', 'batz')
3
ssah

Oracle では、次のようにコレクションを使用できます。

WHERE EXISTS (SELECT 1
                FROM TABLE(ku$_vcnt('bla%', '%foo%', 'batz%'))
               WHERE something LIKE column_value)

ここでは定義済みのコレクション型ku$_vcntを使いましたが、あなたはこのようにあなた自身のものを宣言することができます:

CREATE TYPE my_collection AS TABLE OF VARCHAR2(4000);
2
mik

SQL Serverの場合は、動的SQLに頼ることができます。

そのような状況ではほとんどの場合、データベースからのデータに基づいてIN句のパラメータがあります。

以下の例は少し「強制」されていますが、これはレガシーデータベースに見られるさまざまな実際のケースと一致する可能性があります。

Person という個人名が単一のフィールドに格納されているテーブル PersonName をFirstName + '' + LastNameとします。あなたは、フィールド NameToSelect table NamesToSelect に保存されているファーストネームのリストからすべての人物を選択する必要があります。

あなたは次のようにそれを行うことができます

-- @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
2
bzamfir

ここではSQl ServerとOracleを使用していますが、これがすべてのRDBMSで可能かどうかに興味があります。

Teradataは LIKE ALL/ANY 構文をサポートします。

_ all _ リスト内のすべての文字列。
_ any _ リスト内の任意の文字列。

┌──────────────────────────────┬────────────────────────────────────┐
│      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%'                    │
└──────────────────────────────┴────────────────────────────────────┘
2
Lukasz Szozda

私はこれを解決する方法を持っているかもしれませんが、SQL Server 2008でしか動作しません。 https://stackoverflow.com/a/7285095/894974 に記載されている行コンストラクターを使用して、like句を使用して「架空の」テーブルに参加できることがわかりました。それはそれよりも複雑に聞こえます、見て:

SELECT [name]
  ,[userID]
  ,[name]
  ,[town]
  ,[email]
FROM usr
join (values ('hotmail'),('gmail'),('live')) as myTable(myColumn) on email like '%'+myTable.myColumn+'%' 

これにより、すべてのユーザーがリストに記載されているような電子メールアドレスを持つことになります。誰にでも役立つことを願っています。問題はしばらく私を悩ませていました。

2
Sander

これはコンマ区切りの値に対して機能します。

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

に評価されます。

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

インデックスを使用したい場合は、最初の'%'文字を省略する必要があります。

1
David F Mayer

Oracle RBDMSでは、 REGEXP_LIKE 関数を使用してこの動作を実現できます。

次のコードは、文字列threeがリスト式one | two | three _に存在するかどうかをテストします。 | four | five(ここで、パイプ "|"記号はOR論理演算を意味します)。

SELECT 'Success !!!' result
FROM dual
WHERE REGEXP_LIKE('three', 'one|two|three|four|five');

RESULT
---------------------------------
Success !!!

1 row selected.

前の式は次のものと同等です。

three=one OR three=two OR three=three OR three=four OR three=five

それで成功するでしょう。

一方、次のテストは失敗します。

SELECT 'Success !!!' result
FROM dual
WHERE REGEXP_LIKE('ten', 'one|two|three|four|five');

no rows selected

10gバージョン以降、Oracleで使用可能な正規表現(REGEXP_ *)に関連する関数がいくつかあります。あなたがオラクルの開発者であり、このトピックに興味があるなら、これは良い始まりであるはずです Oracle Databaseでの正規表現の使用

1
jackattack

MySQLを使っているなら、全文検索が一番いいでしょう。

全文検索、MySQLのドキュメント

1
chris

こんな答えはない:

SELECT * FROM table WHERE something LIKE ('bla% %foo% batz%')

Oracleでは問題ありません。

0
Hong Van Vit

あなたはこのような組み合わせを考えるかもしれません:

SELECT  * 
FROM    table t INNER JOIN
(
  SELECT * FROM (VALUES('bla'),('foo'),('batz')) AS list(col)
) l ON t.column  LIKE '%'+l.Col+'%'

ターゲットテーブルにフルテキストインデックスを定義している場合は、次の代替手段を使用できます。

SELECT  * 
FROM    table t
WHERE CONTAINS(t.column, '"bla*" OR "foo*" OR "batz*"')
0
Humayoun_Kabir

2016年以降、SQL ServerにはSTRING_SPLIT関数 が含まれています。私はSQL Server v17.4を使用していますが、これがうまくいくようになっています。

DECLARE @dashboard nvarchar(50)
SET @dashboard = 'P1%,P7%'

SELECT * from Project p
JOIN STRING_SPLIT(@dashboard, ',') AS sp ON p.ProjectNumber LIKE sp.value
0
Mark

Teradataでは、LIKE ANY ('%ABC%','%PQR%','%XYZ%')を使用できます。以下は私に同じ結果をもたらした例です

--===========
--  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%')
0
Piyush Verma