Существует ли комбинация "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 ответ:
в 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+'%'
это приведет к тому, что все пользователи будут иметь адреса электронной почты, подобные тем, которые указаны в списке. Надеюсь, это кому-нибудь пригодится. Эта проблема беспокоит меня в то время как.
это работает для значений, разделенных запятыми
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