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