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でも可能であるかどうかに興味があります。
SQLにはLIKE&INの組み合わせはありませんが、TSQL(SQL Server)またはPLSQL(Oracle)にはそれほどありません。その理由の一部は、全文検索(FTS)が推奨される代替手段であるためです。
OracleとSQL Serverの両方のFTS実装でCONTAINSキーワードがサポートされていますが、構文は若干異なります。
WHERE CONTAINS(t.something, 'bla OR foo OR batz', 1) > 0
WHERE CONTAINS(t.something, '"bla*" OR "foo*" OR "batz*"')
参照:
文を読みやすくするには、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述語を使い続けます。そのため、例は面白かったです。
あなたは立ち往生している
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)
PostgreSQLには ANY
またはALL
の形式があります。
WHERE col LIKE ANY( subselect )
または
WHERE col LIKE ALL( subselect )
副選択が正確に1列のデータを戻すところ。
別の解決策は、あらゆる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)
上記の内部結合または一時テーブルの手法をカプセル化したい場合は、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]
1つの方法は、条件を一時テーブル(またはSQL Serverのテーブル変数)に格納して、次のように結合することです。
SELECT t.SomeField
FROM YourTable t
JOIN #TempTableWithConditions c ON t.something LIKE c.ConditionValue
代わりに内部結合を使用してください。
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
あなたもこれを試すことができます
関数
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%}')
私はそのようなことについても疑問に思いました。私はSUBSTRING
とIN
を組み合わせてテストしたところで、これはこの種の問題に対する効果的な解決策です。以下のクエリを試してください。
Select * from TB_YOUR T1 Where SUBSTRING(T1.Something, 1,3) IN ('bla', 'foo', 'batz')
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);
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
ここでは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%' │ └──────────────────────────────┴────────────────────────────────────┘
私はこれを解決する方法を持っているかもしれませんが、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+'%'
これにより、すべてのユーザーがリストに記載されているような電子メールアドレスを持つことになります。誰にでも役立つことを願っています。問題はしばらく私を悩ませていました。
これはコンマ区切りの値に対して機能します。
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%')
インデックスを使用したい場合は、最初の'%'
文字を省略する必要があります。
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での正規表現の使用 。
MySQLを使っているなら、全文検索が一番いいでしょう。
こんな答えはない:
SELECT * FROM table WHERE something LIKE ('bla% %foo% batz%')
Oracleでは問題ありません。
あなたはこのような組み合わせを考えるかもしれません:
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*"')
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
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%')