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

21 264

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

после использования функции разделения, определенной по адресу: 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)

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

функции

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