有没有&;like &;和“;IN"在SQL ?

在SQL中,我(很遗憾)经常不得不使用“LIKE”条件,因为数据库违反了几乎所有的规范化规则。我现在改变不了。但这与问题无关。

此外,我经常使用WHERE something in (1,1,2,3,5,8,13,21)这样的条件来提高SQL语句的可读性和灵活性。

有没有可能在不编写复杂的子选择的情况下将这两者结合起来?

我想要一些像WHERE something LIKE ('bla%', '%foo%', 'batz%')一样简单的东西,而不是这样:

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

我在这里与SQl Server和Oracle一起工作,但我感兴趣的是,这是否可能在任何RDBMS中。

719687 次浏览

你被困在

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)

一种方法是将条件存储在临时表(或SQL Server中的表变量)中,并像这样连接到它:

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

如果您希望语句易于阅读,那么可以使用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谓词。这些例子只是为了好玩。

使用内部连接代替:

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

如果您想封装上面所示的Inner Join或临时表技术,我建议使用TableValue用户函数。这样可以看得更清楚一点。

在使用定义在:http://www.logiclabz.com/sql-server/split-function-in-sql-server-to-break-comma-separated-strings-into-table.aspx的split函数后

我们可以根据我创建的名为“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   Bass
2   Pike
7   Angler
8   Walleye

LIKE &在SQL中,更不用说在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*"')

要查询的列必须是全文索引的。

参考:

在PostgreSQL中有__ABC0或ALL形式:

WHERE col LIKE ANY( subselect )

WHERE col LIKE ALL( subselect )

其中子选择只返回一列数据。

如果你正在使用MySQL,你可以得到的最接近全文搜索:

全文检索,MySQL文档

我可能对此有一个解决方案,尽管据我所知,它只在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%')

如果你想使用索引,你必须省略第一个'%'字符。

这样做

WHERE something + '%' in ('bla', 'foo', 'batz')
OR '%' + something + '%' in ('tra', 'la', 'la')

WHERE something + '%' in (select col from table where ....)

你甚至可以试试这个

函数

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

对于Sql Server,您可以求助于动态Sql。

大多数情况下,在这种情况下,你有基于数据库中的一些数据的in子句参数。

下面的示例有点“勉强”,但这可以匹配遗留数据库中发现的各种真实情况。

假设你有一个表,其中人名存储在一个字段PersonName中,形式为FirstName + ' ' + LastName。 你需要从一个名字列表中选择所有人,存储在表NamesToSelect的字段NameToSelect中,加上一些附加的条件(如性别,出生日期等)

你可以这样做

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

我也想知道类似的东西。我刚刚使用SUBSTRINGIN的组合进行了测试,它是这种问题的有效解决方案。试试下面的查询:

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

甲骨文中,可以以以下方式使用集合:

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);

另一个解决方案,应该适用于任何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)

内部选择可以用另一个模式源替换,比如表(或视图),如下所示:

WHERE EXISTS (SELECT 1
FROM table_of_patterns t
WHERE something LIKE t.pattern)

table_of_patterns应该至少包含一个列pattern,并且可以像这样填充:

INSERT INTO table_of_patterns(pattern) VALUES ('bla%');
INSERT INTO table_of_patterns(pattern) VALUES ('%foo%');
INSERT INTO table_of_patterns(pattern) VALUES ('batz%');

在Oracle RBDMS中,您可以使用REGEXP_LIKE函数来实现此行为。

下面的代码将测试字符串< em > 3 < / em >是否出现在列表表达式< em > < /一个em > | < em >两个< / em > | < em > 3 < / em > | < em >四< / em > | < em > < / em > 5中(其中管道“|”符号表示或逻辑操作)。

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开发人员,对这个话题感兴趣,这应该是一个很好的开始。

我有一个简单的解决方案,至少在postgresql中工作,使用like any后跟regex列表。这里有一个例子,看看在清单中识别一些抗生素:

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

没有这样的回答:

SELECT * FROM table WHERE something LIKE ('bla% %foo% 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

我在这里与SQl Server和Oracle一起工作,但我感兴趣的是,这是否可能在任何RDBMS中。

Teradata支持像所有/语法:

所有列表中的每个字符串。
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%'                    │
└──────────────────────────────┴────────────────────────────────────┘

编辑:

jOOQ 3.12.0版本支持该语法:

添加合成[NOT] LIKE ANY和[NOT] LIKE ALL操作符

很多时候,SQL用户希望能够组合like和IN谓词,例如:

SELECT *
FROM customer
WHERE last_name [ NOT ] LIKE ANY ('A%', 'E%') [ ESCAPE '!' ]

解决方法是手动将谓词展开为等效谓词

SELECT *
FROM customer
WHERE last_name LIKE 'A%'
OR last_name LIKE 'E%'

jOOQ可以开箱即用地支持这样一个合成谓词。


PostgreSQL LIKE/ILIKE ANY (ARRAY[]):

SELECT *
FROM t
WHERE c LIKE ANY (ARRAY['A%', '%B']);


SELECT *
FROM t
WHERE c LIKE ANY ('{"Do%", "%at"}');

db<>小提琴demo


雪花还支持像任何/像所有的匹配:

像任何/所有

允许基于与或者更多的图案。比较的字符串进行区分大小写的匹配

<subject> LIKE ANY (<pattern1> [, <pattern2> ... ] ) [ ESCAPE <escape_char> ]

例子:

SELECT *
FROM like_example
WHERE subject LIKE ANY ('%Jo%oe%','T%e')
-- WHERE subject LIKE ALL ('%Jo%oe%','J%e')

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

可能你想的组合是这样的:

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*"')

很抱歉挖出了一个旧帖子,但它有很多观点。这周我遇到了一个类似的问题,我想到了这个模式:

declare @example table ( sampletext varchar( 50 ) );


insert @example values
( 'The quick brown fox jumped over the lazy dog.' ),
( 'Ask not what your country can do for you.' ),
( 'Cupcakes are the new hotness.' );


declare @filter table ( searchtext varchar( 50 ) );


insert @filter values
( 'lazy' ),
( 'hotness' ),
( 'cupcakes' );


-- Expect to get rows 1 and 3, but no duplication from Cupcakes and Hotness
select *
from @example e
where exists ( select * from @filter f where e.sampletext like '%' + searchtext + '%' )

Exists()比join()工作得稍微好一点,因为它只测试集合中的每个记录,但如果有多个匹配则不会导致重复。

这可以在Postgres中使用likeilikeanyallarray实现。这是我使用Postgres 9的一个例子:

select id, name from tb_organisation where name ilike any (array['%wembley%', '%south%']);

然后打印出来:

id  |          name
-----+------------------------
433 | South Tampa Center
613 | South Pole
365 | Bromley South
796 | Wembley Special Events
202 | Southall
111 | Wembley Inner Space

T-SQL中,此选项有效,但速度不是很快:

CREATE FUNCTION FN_LIKE_IN (@PROC NVARCHAR(MAX), @ITENS NVARCHAR(MAX)) RETURNS NVARCHAR(MAX) AS BEGIN
    

--Search an item with LIKE inside a list delimited by "," Vathaire 11/06/2019
    

DECLARE @ITEM NVARCHAR(MAX)
WHILE CHARINDEX(',', @ITENS) > 0 BEGIN
SET @ITEM = LEFT(@ITENS, CHARINDEX(',', @ITENS) - 1)
--IF @ITEM LIKE @PROC
IF @PROC LIKE @ITEM
RETURN @PROC --@ITEM --1
ELSE
SET @ITENS = STUFF(@ITENS, 1, LEN(@ITEM) + 1, '')
END
IF @PROC LIKE @ITENS RETURN @PROC --@ITEM --1
RETURN NULL --0
END

查询:

SELECT * FROM SYS.PROCEDURES
WHERE DBO.FN_LIKE_IN(NAME, 'PRC%,SP%') IS NOT NULL

您可以以牺牲性能为代价,为大量元素动态地执行此操作,但这是可行的。

DECLARE @val nvarchar(256),
@list nvarchar(max) = 'one,two,three,ten,five';
CREATE table #table  (FIRST_NAME nvarchar(512), LAST_NAME nvarchar(512));
CREATE table #student  (FIRST_NAME nvarchar(512), LAST_NAME nvarchar(512), EMAIL
nvarchar(512));
INSERT INTO #student (FIRST_NAME, LAST_NAME, EMAIL)
SELECT 'TEST', ' redOne' ,'test.redOne@toto.com' UNION ALL
SELECT 'student', ' student' ,'student@toto.com' UNION ALL
SELECT 'student', ' two' ,'student.two@toto.com' UNION ALL
SELECT 'hello', ' ONE TWO THREE' ,'student.two@toto.com'


DECLARE check_cursor CURSOR FOR select value from STRING_SPLIT(@list,',')


OPEN check_cursor
FETCH NEXT FROM check_cursor INTO @val
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT @val
IF EXISTS (select * from #student where REPLACE(FIRST_NAME, ' ','')
like '%' + @val + '%' OR REPLACE(LAST_NAME, ' ','') like '%' + @val + '%')
BEGIN
INSERT INTO #table (FIRST_NAME, LAST_NAME )
SELECT TOP 1 FIRST_NAME, LAST_NAME VALUE from #student where
REPLACE(FIRST_NAME, ' ','') like '%' + @val + '%' OR REPLACE(LAST_NAME, ' ','')
like '%' + @val + '%'
END;


FETCH NEXT FROM check_cursor INTO @val
END
CLOSE check_cursor;
DEALLOCATE check_cursor;


SELECT * FROM #table;
DROP TABLE #table;
DROP TABLE #student;

在SQL SERVER中使用游标并执行每一个值:

表样例:

 create table Gastos_ConciliacionExcluida(IdRegistro int identity(1,1), MascaraTexto nvarchar(50), Activa bit default 1, Primary key (IDRegistro))




insert into Gastos_ConciliacionExcluida(MascaraTexto) Values ('%Reembolso%')






alter procedure SP_Gastos_ConciliacionExcluidaProcesar
as


declare cur cursor for select MascaraTexto From Gastos_ConciliacionExcluida where Activa=1
declare @Txt nvarchar(50)


open cur


fetch next from cur into @Txt
while @@Fetch_Status = 0
begin
update Gastos_BancoRegistro set PresumibleNoConciliable = 1
where   Concepto like @txt
fetch next from cur into @Txt
end
close cur
deallocate cur