two - sql like in sql server
¿Existe una combinación de "LIKE" e "IN" en SQL? (21)
Estoy trabajando con SQl Server y Oracle aquí, pero estoy interesado si esto es posible en cualquier RDBMS.
Teradata soporta la sintaxis de LIKE ALL/ANY
:
TODAS las cadenas en la lista.
CUALQUIER cadena en la lista.
┌──────────────────────────────┬────────────────────────────────────┐ │ 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%'' │ └──────────────────────────────┴────────────────────────────────────┘
En SQL I (lamentablemente) a menudo tengo que usar condiciones " LIKE
" debido a las bases de datos que violan casi todas las reglas de normalización. No puedo cambiar eso ahora. Pero eso es irrelevante para la pregunta.
Además, a menudo utilizo condiciones como WHERE something in (1,1,2,3,5,8,13,21)
para una mejor legibilidad y flexibilidad de mis declaraciones SQL.
¿Hay alguna forma posible de combinar estas dos cosas sin escribir subselecciones complicadas?
Quiero algo tan fácil como WHERE something LIKE (''bla%'', ''%foo%'', ''batz%'')
como WHERE something LIKE (''bla%'', ''%foo%'', ''batz%'')
lugar de esto:
WHERE something LIKE ''bla%''
OR something LIKE ''%foo%''
OR something LIKE ''batz%''
Estoy trabajando con SQl Server y Oracle aquí, pero estoy interesado si esto es posible en cualquier RDBMS.
A partir de 2016, SQL Server incluye una function STRING_SPLIT
. Estoy usando SQL Server v17.4 y conseguí que esto funcionara para mí:
DECLARE @dashboard nvarchar(50)
SET @dashboard = ''P1%,P7%''
SELECT * from Project p
JOIN STRING_SPLIT(@dashboard, '','') AS sp ON p.ProjectNumber LIKE sp.value
Con PostgreSQL existe la forma ANY
o ALL
:
WHERE col LIKE ANY( subselect )
o
WHERE col LIKE ALL( subselect )
donde la subselección devuelve exactamente una columna de datos.
En Oracle puedes usar una colección de la siguiente manera:
WHERE EXISTS (SELECT 1
FROM TABLE(ku$_vcnt(''bla%'', ''%foo%'', ''batz%''))
WHERE something LIKE column_value)
Aquí he usado un tipo de colección predefinido ku$_vcnt
, pero puedes declarar tu propio como este:
CREATE TYPE my_collection AS TABLE OF VARCHAR2(4000);
En Oracle RBDMS puede lograr este comportamiento utilizando la función REGEXP_LIKE .
El siguiente código probará si la cadena tres está presente en la expresión de lista uno | dos | tres | cuatro | cinco (en el que el símbolo de la tubería " | " significa operación lógica O).
SELECT ''Success !!!'' result
FROM dual
WHERE REGEXP_LIKE(''three'', ''one|two|three|four|five'');
RESULT
---------------------------------
Success !!!
1 row selected.
La expresión precedente es equivalente a:
three=one OR three=two OR three=three OR three=four OR three=five
Así tendrá éxito.
Por otro lado, la siguiente prueba fallará.
SELECT ''Success !!!'' result
FROM dual
WHERE REGEXP_LIKE(''ten'', ''one|two|three|four|five'');
no rows selected
Hay varias funciones relacionadas con expresiones regulares (REGEXP_ *) disponibles en Oracle desde la versión 10g. Si usted es un desarrollador de Oracle y está interesado en este tema, este debería ser un buen comienzo. Usar expresiones regulares con la base de datos Oracle .
Esto funciona para valores separados por comas.
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 ''''%'') + ''%'''')''''
Evalúa a:
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%'')
Si desea que use índices, debe omitir el primer carácter ''%''
.
No hay respuesta como esta:
SELECT * FROM table WHERE something LIKE (''bla% %foo% batz%'')
En el oráculo no hay problema.
No hay una combinación de LIKE & IN en SQL, y mucho menos en TSQL (SQL Server) o PLSQL (Oracle). Parte de la razón de esto es que la Búsqueda de texto completo (FTS) es la alternativa recomendada.
Las implementaciones de Oracle y SQL Server FTS son compatibles con la palabra clave CONTAINS, pero la sintaxis sigue siendo ligeramente diferente:
Oráculo:
WHERE CONTAINS(t.something, ''bla OR foo OR batz'', 1) > 0
Servidor SQL:
WHERE CONTAINS(t.something, ''"bla*" OR "foo*" OR "batz*"'')
Referencia:
Otra solución, debería funcionar en cualquier RDBMS:
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)
Para el servidor SQL puede recurrir a SQL dinámico.
La mayoría de las veces, en tales situaciones, tiene el parámetro de la cláusula IN basado en algunos datos de la base de datos.
El siguiente ejemplo es un poco "forzado", pero puede coincidir con varios casos reales encontrados en bases de datos heredadas.
Supongamos que tiene personas de la tabla donde los nombres de las personas se almacenan en un solo campo Nombre de la persona como Nombre + '''' + Apellido. Debe seleccionar a todas las personas de una lista de nombres, almacenados en el campo NameToSelect en la tabla NamesToSelect , más algunos criterios adicionales (como filtrado por género, fecha de nacimiento, etc.)
Puedes hacerlo de la siguiente manera
-- @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
Puede que tenga una solución para esto, aunque solo funcionará en SQL Server 2008 por lo que sé. Descubrí que puedes usar el constructor de filas descrito en https://.com/a/7285095/894974 para unirte a una tabla ''ficticia'' usando una cláusula similar. Suena más complejo de lo que es, mira:
SELECT [name]
,[userID]
,[name]
,[town]
,[email]
FROM usr
join (values (''hotmail''),(''gmail''),(''live'')) as myTable(myColumn) on email like ''%''+myTable.myColumn+''%''
Esto dará como resultado que todos los usuarios tengan una dirección de correo electrónico como las que se proporcionan en la lista. Espero que sea de utilidad para cualquiera. El problema me había estado molestando un rato.
Si desea que su declaración sea fácil de leer, entonces puede usar REGEXP_LIKE (disponible desde la versión 10 de Oracle en adelante).
Una tabla de ejemplo:
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.
La sintaxis original:
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.
Y una consulta de aspecto simple con REGEXP_LIKE
SQL> select something
2 from mytable
3 where regexp_like (something,''^bla|foo|^batz'')
4 /
SOMETH
------
blabla
ofooof
batzzz
3 rows selected.
PERO ...
No lo recomendaría yo mismo debido al rendimiento no tan bueno. Me quedo con los varios predicados de LIKE. Así que los ejemplos fueron solo por diversión.
Si está utilizando MySQL, lo más cercano que puede obtener es la búsqueda de texto completo:
También me preguntaba por algo así. Acabo de realizar una prueba utilizando una combinación de SUBSTRING
e IN
y es una solución efectiva para este tipo de problema. Pruebe la siguiente consulta:
Select * from TB_YOUR T1 Where SUBSTRING(T1.Something, 1,3) IN (''bla'', ''foo'', ''batz'')
Tengo una solución simple, que funciona en postgresql al menos, usando like any
seguido por la lista de expresiones regulares. Aquí hay un ejemplo, mirando a identificar algunos antibióticos en una lista:
select *
from database.table
where lower(drug_name) like any (''{%cillin%,%cyclin%,%xacin%,%mycine%,%cephal%}'')
Un enfoque sería almacenar las condiciones en una tabla temporal (o variable de tabla en SQL Server) y unirse a eso de esta manera:
SELECT t.SomeField
FROM YourTable t
JOIN #TempTableWithConditions c ON t.something LIKE c.ConditionValue
Utilice una unión interna en su lugar:
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
Yo sugeriría usar una función de usuario TableValue si desea encapsular las técnicas de la combinación interna o de la tabla temporal que se muestran arriba. Esto le permitiría leer un poco más claro.
Después de usar la función de división definida en: http://www.logiclabz.com/sql-server/split-function-in-sql-server-to-break-comma-separated-strings-into-table.aspx
Podemos escribir lo siguiente en base a una tabla que creé llamada "Fish" (int id, varchar (50) Name)
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.
Salidas
1 Bass 2 Pike 7 Angler 8 Walleye
estás atrapado con el
WHERE something LIKE ''bla%''
OR something LIKE ''%foo%''
OR something LIKE ''batz%''
a menos que complete una tabla temporal (incluya los comodines con los datos) y únase de esta manera:
FROM YourTable y
INNER JOIN YourTempTable t On y.something LIKE t.something
Pruébelo (usando la sintaxis de 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
SALIDA:
x
----------
abcdefg
abc
(2 row(s) affected)
x
----------
abc
abcdefg
(2 row(s) affected)
hacer esto
WHERE something + ''%'' in (''bla'', ''foo'', ''batz'')
OR ''%'' + something + ''%'' in (''tra'', ''la'', ''la'')
o
WHERE something + ''%'' in (select col from table where ....)
incluso puedes probar esto
Función
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
Consulta
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+''%'';