In SQL muss ich (leider) oft "LIKE
" -Bedingungen verwenden, da Datenbanken gegen nahezu jede Regel der Normalisierung verstoßen. Ich kann das jetzt nicht ändern. Aber das ist für die Frage irrelevant.
Außerdem verwende ich häufig Bedingungen wie WHERE something in (1,1,2,3,5,8,13,21)
, um die Lesbarkeit und Flexibilität meiner SQL-Anweisungen zu verbessern.
Gibt es eine Möglichkeit, diese beiden Dinge zu kombinieren, ohne komplizierte Unterauswahlen zu schreiben?
Ich möchte etwas so einfaches wie WHERE something LIKE ('bla%', '%foo%', 'batz%')
statt dessen:
WHERE something LIKE 'bla%'
OR something LIKE '%foo%'
OR something LIKE 'batz%'
Ich arbeite hier mit SQl Server und Oracle, bin aber interessiert, ob dies in einem RDBMS überhaupt möglich ist.
Es gibt keine Kombination aus LIKE & IN in SQL, und noch weniger in TSQL (SQL Server) oder PLSQL (Oracle). Ein Grund dafür ist, dass die Volltextsuche (FTS) die empfohlene Alternative ist.
Sowohl Oracle- als auch SQL Server-FTS-Implementierungen unterstützen das Schlüsselwort CONTAINS. Die Syntax unterscheidet sich jedoch immer noch geringfügig:
WHERE CONTAINS(t.something, 'bla OR foo OR batz', 1) > 0
WHERE CONTAINS(t.something, '"bla*" OR "foo*" OR "batz*"')
Referenz:
Wenn Sie Ihre Aussage leicht lesbar machen möchten, können Sie REGEXP_LIKE (verfügbar ab Oracle Version 10) verwenden.
Eine Beispieltabelle:
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.
Die ursprüngliche Syntax:
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.
Und eine einfach aussehende Abfrage mit REGEXP_LIKE
SQL> select something
2 from mytable
3 where regexp_like (something,'^bla|foo|^batz')
4 /
SOMETH
------
blabla
ofooof
batzzz
3 rows selected.
ABER ...
Ich würde es mir aufgrund der nicht so guten Leistung nicht empfehlen. Ich würde bei den verschiedenen LIKE-Prädikaten bleiben. Die Beispiele waren also nur zum Spaß.
du bleibst beim
WHERE something LIKE 'bla%'
OR something LIKE '%foo%'
OR something LIKE 'batz%'
es sei denn, Sie füllen eine temporäre Tabelle aus (schließen Sie die Platzhalter mit den Daten ein) und fügen Sie folgendermaßen hinzu
FROM YourTable y
INNER JOIN YourTempTable t On y.something LIKE t.something
probieren Sie es aus (mit SQL Server-Syntax):
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
AUSGABE:
x
----------
abcdefg
abc
(2 row(s) affected)
x
----------
abc
abcdefg
(2 row(s) affected)
Bei PostgreSQL gibt es die Form ANY
oder ALL
:
WHERE col LIKE ANY( subselect )
oder
WHERE col LIKE ALL( subselect )
wo die Unterauswahl genau eine Datenspalte zurückgibt.
Ich würde vorschlagen, eine TableValue-Benutzerfunktion zu verwenden, wenn Sie die oben gezeigten Inner Join- oder Temp-Table-Techniken kapseln möchten. Dies würde es ermöglichen, etwas klarer zu lesen.
Nach der Verwendung der aufgeteilten Funktion: http://www.logiclabz.com/sql-server/split-function-in-sql-server-to-break-comma-separated-strings-into-table.aspx
wir können Folgendes basierend auf einer von mir erstellten Tabelle mit dem Namen "Fish" schreiben (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.
Ausgänge
1 Bass 2 Pike 7 Angler 8 Zander
Eine andere Lösung sollte in jedem RDBMS funktionieren:
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)
Ein Ansatz wäre, die Bedingungen in einer temporären Tabelle (oder Tabellenvariablen in SQL Server) zu speichern und sich wie folgt anzuschließen:
SELECT t.SomeField
FROM YourTable t
JOIN #TempTableWithConditions c ON t.something LIKE c.ConditionValue
Verwenden Sie stattdessen eine innere Verknüpfung:
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
sie können dies sogar versuchen
Funktion
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
Abfrage
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+'%';
Ich habe eine einfache Lösung, die in postgresql funktioniert, zumindest mit like any
gefolgt von der Liste der regulären Ausdrücke. Hier ein Beispiel, in dem einige Antibiotika in einer Liste aufgeführt werden:
select *
from database.table
where lower(drug_name) like any ('{%cillin%,%cyclin%,%xacin%,%mycine%,%cephal%}')
Ich habe mich auch über so etwas gewundert. Ich habe gerade mit einer Kombination aus SUBSTRING
und IN
getestet, und es ist eine effektive Lösung für diese Art von Problem. Versuchen Sie die folgende Abfrage:
Select * from TB_YOUR T1 Where SUBSTRING(T1.Something, 1,3) IN ('bla', 'foo', 'batz')
In Oracle können Sie eine Sammlung auf folgende Weise verwenden:
WHERE EXISTS (SELECT 1
FROM TABLE(ku$_vcnt('bla%', '%foo%', 'batz%'))
WHERE something LIKE column_value)
Hier habe ich einen vordefinierten Collection-Typ ku$_vcnt
verwendet, aber Sie können Ihren eigenen Typ wie folgt angeben:
CREATE TYPE my_collection AS TABLE OF VARCHAR2(4000);
Für SQL Server können Sie auf Dynamic SQL zurückgreifen.
In solchen Situationen haben Sie meistens den Parameter IN-Klausel, der auf Daten aus der Datenbank basiert.
Das folgende Beispiel ist etwas "erzwungen", dies kann jedoch mit verschiedenen realen Fällen in älteren Datenbanken übereinstimmen.
Angenommen, Sie haben eine Tabelle Persons , in der Personennamen in einem einzigen Feld PersonName als Vorname + '' + Nachname gespeichert sind. Sie müssen alle Personen aus einer Liste von Vornamen auswählen, die im Feld NameToSelect in der Tabelle NamesToSelect gespeichert sind, sowie einige zusätzliche Kriterien (z. B. nach Geschlecht, Geburtsdatum usw.).
Sie können es wie folgt tun
-- @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
Ich arbeite hier mit SQl Server und Oracle, aber ich bin daran interessiert, ob dies in einem beliebigen RDBMS überhaupt möglich ist.
Teradata unterstützt LIKE ALL/ANY
Syntax:
ALL jeder String in der Liste.
ANY eine beliebige Zeichenfolge in der Liste.┌──────────────────────────────┬────────────────────────────────────┐ │ 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%' │ └──────────────────────────────┴────────────────────────────────────┘
Ich habe möglicherweise eine Lösung dafür, obwohl es meines Wissens nur in SQL Server 2008 funktioniert. Ich habe festgestellt, dass Sie den in https://stackoverflow.com/a/7285095/894974 beschriebenen Zeilenkonstruktor verwenden können, um eine "fiktive" Tabelle mit einer Like-Klausel zu verbinden ..__ Es klingt komplexer ist, schau:
SELECT [name]
,[userID]
,[name]
,[town]
,[email]
FROM usr
join (values ('hotmail'),('gmail'),('live')) as myTable(myColumn) on email like '%'+myTable.myColumn+'%'
Dies führt dazu, dass alle Benutzer mit einer E-Mail-Adresse wie der in der Liste angegebenen Adresse versehen werden. Hoffen Sie, dass sie für jeden von Nutzen ist. Das Problem hatte mich eine Weile gestört.
Dies funktioniert bei durch Kommas getrennten Werten
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 ''%') + '%'')''
Bewertet zu:
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%')
Wenn Sie Indizes verwenden möchten, müssen Sie das erste '%'
-Zeichen weglassen.
In Oracle RBDMS können Sie dieses Verhalten mit der Funktion REGEXP_LIKE erreichen.
Der folgende Code prüft, ob die Zeichenfolge three im Listenausdruck one | two | three vorhanden ist. | vier | fünf (wobei das Pipe-Symbol "|" OR Logikoperation bedeutet).
SELECT 'Success !!!' result
FROM dual
WHERE REGEXP_LIKE('three', 'one|two|three|four|five');
RESULT
---------------------------------
Success !!!
1 row selected.
Der vorangegangene Ausdruck ist äquivalent zu:
three=one OR three=two OR three=three OR three=four OR three=five
So wird es gelingen.
Der folgende Test schlägt jedoch fehl.
SELECT 'Success !!!' result
FROM dual
WHERE REGEXP_LIKE('ten', 'one|two|three|four|five');
no rows selected
In Oracle stehen seit der 10g-Version mehrere Funktionen für reguläre Ausdrücke (REGEXP_ *) zur Verfügung. Wenn Sie ein Oracle-Entwickler sind und sich für dieses Thema interessieren, sollte dies ein guter Anfang sein Verwenden von regulären Ausdrücken mit Oracle-Datenbank .
Wenn Sie MySQL verwenden, ist die Volltextsuche am nächsten:
Keine Antwort wie folgt:
SELECT * FROM table WHERE something LIKE ('bla% %foo% batz%')
In Oracle kein Problem.
Vielleicht denkst du die Kombination so:
SELECT *
FROM table t INNER JOIN
(
SELECT * FROM (VALUES('bla'),('foo'),('batz')) AS list(col)
) l ON t.column LIKE '%'+l.Col+'%'
Wenn Sie einen Volltextindex für Ihre Zieltabelle definiert haben, können Sie diese Alternative verwenden:
SELECT *
FROM table t
WHERE CONTAINS(t.column, '"bla*" OR "foo*" OR "batz*"')
Ab 2016 enthält SQL Server eine STRING_SPLIT
Funktion . Ich verwende SQL Server v17.4 und habe das für mich zum Laufen gebracht:
DECLARE @dashboard nvarchar(50)
SET @dashboard = 'P1%,P7%'
SELECT * from Project p
JOIN STRING_SPLIT(@dashboard, ',') AS sp ON p.ProjectNumber LIKE sp.value
In Teradata können Sie LIKE ANY ('%ABC%','%PQR%','%XYZ%')
verwenden. Unten ist ein Beispiel, das für mich zu den gleichen Ergebnissen geführt hat
--===========
-- 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%')