Есть ли в SQL комбинация "LIKE" и "IN"? - PullRequest
302 голосов
/ 10 июня 2010

В 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, но мне интересно, еслиэто возможно в любой СУБД.

Ответы [ 23 ]

3 голосов
/ 28 декабря 2016

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

select *
from database.table
where lower(drug_name) like any ('{%cillin%,%cyclin%,%xacin%,%mycine%,%cephal%}')
2 голосов
/ 10 сентября 2018

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

Teradata поддерживает LIKE ALL/ANY синтаксис:

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%'                    │
└──────────────────────────────┴────────────────────────────────────┘

РЕДАКТИРОВАТЬ:

jOOQ версии 3.12.0 поддерживает этот синтаксис:

Добавить синтетический [NOT] LIKE ANY и [NOT] LIKEВСЕ операторы

Часто пользователи SQL хотели бы иметь возможность комбинировать предикаты LIKE и IN, например:

SELECT *
FROM customer
WHERE last_name [ NOT ] LIKE ANY ('A%', 'E%') [ ESCAPE '!' ]

Временное решение - вручнуюразверните предикат до эквивалентного

SELECT *
FROM customer
WHERE last_name LIKE 'A%'
OR last_name LIKE 'E%'

jOOQ может поддерживать такой синтетический предикат из коробки.

2 голосов
/ 16 мая 2016

В 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);
2 голосов
/ 15 июня 2015

Для Sql Server вы можете использовать динамический SQL.

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

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

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

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

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

1 голос
/ 26 декабря 2016

В Oracle RBDMS вы можете добиться этого поведения, используя функцию REGEXP_LIKE .

Следующий код проверит наличие строки three в списке выражение один | два | три | четыре | пять (в котором символ " | " обозначает логическую операцию ИЛИ).

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

Существует несколько функций, связанных с регулярными выражениями (REGEXP_ *), доступных в Oracle начиная с версии 10g.Если вы являетесь разработчиком Oracle и интересуетесь этой темой, это будет хорошим началом Использование регулярных выражений с базой данных Oracle .

1 голос
/ 09 июля 2013

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

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

1 голос
/ 19 августа 2014

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

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

Если вы хотите, чтобы он использовал индексы, вы должны опустить первый '%' символ.

0 голосов
/ 17 июня 2019

Может быть, вы думаете, что эта комбинация выглядит так:

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*"')
0 голосов
/ 04 декабря 2018

В 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%')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...