Есть ли комбинация "LIKE" и " IN " в SQL?

в SQL I (к сожалению) часто приходится использовать "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 здесь, но мне интересно, возможно ли это в любой СУБД вообще.

21 ответов


в 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 Server:

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.

но ...

Я бы не рекомендовал его сам из-за не очень хорошей производительности. Я бы придерживался нескольких подобных предикатов. Так что примеры были просто ради забавы.


вы застряли с

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 )

где подвыбор возвращает ровно один столбец данных.


Я бы предложил использовать пользовательскую функцию TableValue, если вы хотите инкапсулировать внутренние методы объединения или временной таблицы, показанные выше. Это позволит ему читать более четко.

после использования функции split, определенной в: http://www.logiclabz.com/sql-server/split-function-in-sql-server-to-break-comma-separated-strings-into-table.aspx

мы можем написать следующее на основе таблицы, которую я создал под названием " Рыба "(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

одним из подходов было бы сохранить условия во временной таблице (или табличной переменной в 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

другое решение, должно работать на любых СУБД:

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)

u может даже попробовать это

функции

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.

большую часть времени в таких ситуациях у вас есть параметр в статье на основе некоторых данных из базы данных.

приведенный ниже пример немного "принудительный", но это может соответствовать различным реальным случаям, найденным в устаревших базах данных.

Предположим, у вас есть таблица человек где имена людей хранятся в одном поле PersonName как имя + '' + фамилия. Вам нужно выбрать все человек из списка имен, хранящихся в поле NameToSelect в таблице 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

мне тоже было интересно что-то подобное. Я только что тестировал, используя комбинацию SUBSTRING и IN и это эффективное решение для такого рода проблемы. Попробуйте следующий запрос:

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

у меня есть простое решение, которое работает в postgresql по крайней мере, используя like any затем следует список регулярных выражений. Вот пример, глядя на выявление некоторых антибиотиков в списке:

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

У меня может быть решение для этого, хотя, насколько я знаю, оно будет работать только в 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+'%' 

это приведет ко всем пользователям с адресами электронной почты, как те, которые указаны в списке. Надеюсь, он кому-нибудь пригодится. Эта проблема беспокоит меня в то время как.


Если вы используете MySQL, ближе всего вы можете получить полнотекстовый поиск:

Полнотекстовый Поиск, Документация MySQL


это работает для значений, разделенных запятыми

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 вы можете использовать коллекцию следующим образом:

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

в Oracle RBDMS вы можете достичь этого поведения, используя REGEXP_LIKE


нет такого ответа:

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

в oracle нет проблем.


Я работаю с SQl Server и Oracle здесь, но мне интересно, возможно ли это в любой СУБД вообще.

Teradata поддерживает LIKE 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%'                    │
└──────────────────────────────┴────────────────────────────────────┘

начиная с 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

этого

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

или

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