SQL中是否有“LIKE”和“IN”的组合?

时间:2010-06-10 13:44:17

标签: sql sql-server oracle tsql plsql

在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中使用它。

25 个答案:

答案 0 :(得分:174)

没有LIKE&组合在SQL中,在TSQL(SQL Server)或PLSQL(Oracle)中更少。部分原因是因为建议使用全文搜索(FTS)。

Oracle和SQL Server FTS实现都支持CONTAINS关键字,但语法仍然略有不同:

甲骨文:

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

SQL Server:

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

参考:

答案 1 :(得分:55)

如果您想使您的声明易于阅读,那么您可以使用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谓词。所以这些例子只是为了好玩。

答案 2 :(得分:43)

你坚持

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)

答案 3 :(得分:18)

对于PostgreSQL,有ANY or ALL form:

WHERE col LIKE ANY( subselect )

WHERE col LIKE ALL( subselect )

其中subselect只返回一列数据。

答案 4 :(得分:12)

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

答案 5 :(得分:10)

如果您想要封装上面显示的Inner Join或temp表技术,我建议使用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)名称)编写以下内容

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

答案 6 :(得分:7)

一种方法是将条件存储在临时表(或SQL Server中的表变量)中并加入到以下内容中:

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

答案 7 :(得分:7)

改为使用内部联接:

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

答案 8 :(得分:4)

你甚至可以尝试这个

功能

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

答案 9 :(得分:3)

我有一个简单的解决方案,至少使用 postgresql ,使用like any后跟正则表达式列表。这是一个例子,看一下列表中的一些抗生素:

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

答案 10 :(得分:3)

我也想知道这样的事情。我刚刚使用SUBSTRINGIN的组合进行了测试,它是解决此类问题的有效方法。请尝试以下查询:

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

答案 11 :(得分:2)

For Sql Server you can resort to Dynamic SQL.

Most of the time in such situations you have the parameter of IN clause based on some data from database.

The example below is a little "forced", but this can match various real cases found in legacy databases.

Suppose you have table Persons where person names are stored in a single field PersonName as FirstName + ' ' + LastName. You need to select all persons from a list of first names, stored in field NameToSelect in table NamesToSelect, plus some additional criteria (like filtered on gender, birth date, etc)

You can do it as follows

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

答案 12 :(得分:2)

  

我在这里使用SQl Server和Oracle,但是我很感兴趣是否可以在任何RDBMS中使用。

Teradata支持LIKE ALL/ANY语法:

  

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

答案 13 :(得分:2)

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

答案 14 :(得分:2)

我可能有这方面的解决方案,但据我所知,它只适用于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+'%' 

这将导致所有用户都拥有列表中提供的电子邮件地址。 希望它对任何人都有用。这个问题困扰了我一段时间。

答案 15 :(得分:1)

这适用于逗号分隔值

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

如果您希望它使用索引,则必须省略第一个'%'字符。

答案 16 :(得分:1)

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

以下代码将测试列表表达式 one | two em> | 三个 | 四个 | 五个 (其中管道“ | ”符号表示或逻辑运算)。

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开发人员并对此主题感兴趣,那么这应该是一个很好的开始Using Regular Expressions with Oracle Database

答案 17 :(得分:1)

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

Full-Text Search, MySQL Documentation

答案 18 :(得分:0)

没有这样的答案:

 public DataSource dataSourceSecond() throws SQLException {
  HikariConfig config = new HikariConfig("D:/hikari2.properties");

在oracle没问题。

答案 19 :(得分:0)

从2016年开始,SQL Server包含STRING_SPLIT function。我使用的是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

答案 20 :(得分:0)

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

答案 21 :(得分:0)

也许您认为这样的组合:

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

答案 22 :(得分:0)

我知道这已经很晚了,但是我也遇到了类似的情况。我需要一个“ Like In”运算符来存储一组存储过程,该存储过程接受许多参数,然后使用这些参数来聚合来自多个RDBMS系统的数据,因此没有RDBMS特定的技巧起作用,但是该存储过程和任何功能它将在MS SQL Server上运行,因此我们可以使用T-SQL来为每个RDBMS生成完整的SQL语句,但是输出必须与RDBMS完全无关。

这是我目前想出的将分隔字符串(例如存储过程中的参数)转换为SQL块的方法。我称其为“ Like IN”的“地衣”。懂吗?

Lichen.sql

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =======================================================================
-- Lichen - Scalar Valued Function
-- Returns nvarchar(512) of "LIKE IN" results.  See further documentation.
-- CREATOR: Norman David Cooke
-- CREATED: 2020-02-05
-- UPDATED:
-- =======================================================================
CREATE OR ALTER FUNCTION Lichen 
(
    -- Add the parameters for the function here
    @leadingAnd bit = 1,
    @delimiter nchar(1) = ';',
    @colIdentifier nvarchar(64),
    @argString nvarchar(256)
)
RETURNS nvarchar(512)
AS
BEGIN
    -- Declare the return variable here
    DECLARE @result nvarchar(512)

    -- set delimiter to detect (add more here to detect a delimiter if one isn't provided)
    DECLARE @delimit nchar(1) = ';'
    IF NOT @delimiter = @delimit 
        SET @delimit = @delimiter


    -- check to see if we have any delimiters in the input pattern
    IF CHARINDEX(@delimit, @argString) > 1  -- check for the like in delimiter
    BEGIN  -- begin 'like in' branch having found a delimiter
        -- set up a table variable and string_split the provided pattern into it.
        DECLARE @lichenTable TABLE ([id] [int] IDENTITY(1,1) NOT NULL, line NVARCHAR(32))
        INSERT INTO @lichenTable SELECT * FROM STRING_SPLIT(@argString, ';')

        -- setup loop iterators and determine how many rows were inserted into lichen table
        DECLARE @loopCount int = 1
        DECLARE @lineCount int 
        SELECT @lineCount = COUNT(*) from @lichenTable

        -- select the temp table (to see whats inside for debug)
        --select * from @lichenTable

        -- BEGIN AND wrapper block for 'LIKE IN' if bit is set
        IF @leadingAnd = 1
            SET @result = ' AND ('
        ELSE
            SET @result = ' ('

        -- loop through temp table to build multiple "LIKE 'x' OR" blocks inside the outer AND wrapper block
        WHILE ((@loopCount IS NOT NULL) AND (@loopCount <= @lineCount))
        BEGIN -- begin loop through @lichenTable
            IF (@loopcount = 1) -- the first loop does not get the OR in front
                SELECT @result = CONCAT(@result, ' ', @colIdentifier, ' LIKE ''', line, '''') FROM @lichenTable WHERE id = @loopCount
            ELSE  -- but all subsequent loops do
                SELECT @result = CONCAT(@result, ' OR ', @colIdentifier, ' LIKE ''', line, '''') FROM @lichenTable WHERE id = @loopCount
            SET @loopcount = @loopCount + 1     -- increment loop
        END -- end loop through @lichenTable

        -- set final parens after lichenTable loop
        SET @result = CONCAT(@result, ' )')
    END  -- end 'like in' branch having found a delimiter
    ELSE -- no delimiter was provided
    BEGIN   -- begin "no delimiter found" branch
        IF @leadingAnd = 1 
            SET @result = CONCAT(' AND ', @colIdentifier, ' LIKE ''' + @argString + '''')
        ELSE
            SET @result = CONCAT(' ', @colIdentifier, ' LIKE ''' + @argString + '''')
    END     -- end "no delimiter found" branch

    -- Return the result of the function
    RETURN @result
END  -- end lichen function

GO

可能已经计划了定界符检测,但是目前它默认为分号,因此您可以在其中放置default。这可能有错误。 @leadingAnd参数只是一个位值,用于确定是否要在块的前面放置“ AND”,以便与其他WHERE子句添加项很好地配合。

用法示例(在argString中带有定界符)

SELECT [dbo].[Lichen] (
   default        -- @leadingAND, bit, default: 1
  ,default        -- @delimiter, nchar(1), default: ';'
  ,'foo.bar'      -- @colIdentifier, nvarchar(64), this is the column identifier
  ,'01%;02%;%03%' -- @argString, nvarchar(256), this is the input string to parse "LIKE IN" from
)
GO

将返回包含以下内容的nvarchar(512):

 AND ( foo.bar LIKE '01%' OR foo.bar LIKE '02%' OR foo.bar LIKE '%03%' ) 

如果输入不包含定界符,它将也跳过该块:

用法示例(在argString中没有定界符)

SELECT [dbo].[Lichen] (
   default        -- @leadingAND, bit, default: 1
  ,default        -- @delimiter, nchar(1), default: ';'
  ,'foo.bar'      -- @colIdentifier, nvarchar(64), this is the column identifier
  ,'01%'          -- @argString, nvarchar(256), this is the input string to parse "LIKE IN" from
)
GO

将返回包含以下内容的nvarchar(512):

 AND foo.bar LIKE '01%'

我将继续进行此工作,因此,如果我忽略了某些内容(显而易见或其他明显的内容),请随时发表评论或联系我们。

答案 23 :(得分:0)

很抱歉疏通旧帖子,但它有很多观点。我本周遇到了类似的问题,并提出了这种模式:

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()比IMO联接要好一些,因为它仅测试集合中的每个记录,但是如果有多个匹配项,则不会导致重复。

答案 24 :(得分:-3)

这样做

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

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