Das Feld SQL SELECT WHERE enthält Wörter


562

Ich brauche eine Auswahl, die folgende Ergebnisse liefert:

SELECT * FROM MyTable WHERE Column1 CONTAINS 'word1 word2 word3'

Und ich brauche alle Ergebnisse, dh dies schließt Zeichenfolgen mit 'Wort2 Wort3 Wort1' oder 'Wort1 Wort3 Wort2' oder einer anderen Kombination der drei ein.

Alle Wörter müssen im Ergebnis enthalten sein.

Antworten:


843

Eher langsame, aber funktionierende Methode, um eines der Wörter einzuschließen:

SELECT * FROM mytable
WHERE column1 LIKE '%word1%'
   OR column1 LIKE '%word2%'
   OR column1 LIKE '%word3%'

Wenn alle Wörter vorhanden sein müssen, verwenden Sie Folgendes:

SELECT * FROM mytable
WHERE column1 LIKE '%word1%'
  AND column1 LIKE '%word2%'
  AND column1 LIKE '%word3%'

Wenn Sie etwas schneller möchten, müssen Sie sich mit der Volltextsuche befassen. Dies ist für jeden Datenbanktyp sehr spezifisch.


3
+ 1 Ich stimme zu, dass es langsamer ist, aber es kann durch eine gute Indizierung gemildert werden
Preet Sangha

12
@PreetSangha Indizierung, wenn Sie nach LIKE suchen, beginnend mit einem Platzhalter? Bitte zeig mir wie!
Popnoodles

1
In PostgreSQL 9.1 und höher können Sie einen Trigrammindex erstellen, der solche Suchvorgänge indizieren kann .
MVP

2
@AquaAlex: Ihre Anweisung schlägt fehl, wenn Text hat word3 word2 word1.
MVP

3
Ein weiterer Nachteil dieses Ansatzes: '% word%' findet auch 'Wörter', 'Kreuzworträtsel' und 'Schwert' (nur als Beispiel). Ich müsste eine Spalte1 WIE 'Wort' ODER Spalte1 WIE 'Wort%' ODER Spalte1 WIE '% Wort' ODER Spalte1 WIE 'Wort' ausführen, um nur genaue Wortübereinstimmungen zu finden - und es würde immer noch für Einträge fehlschlagen, bei denen Wörter nicht sind nur mit Leerzeichen getrennt.
BlaM

81

Beachten Sie, dass Sie, wenn Sie LIKEbestimmen möchten, ob eine Zeichenfolge eine Teilzeichenfolge einer anderen Zeichenfolge ist, die Mustervergleichszeichen in Ihrer Suchzeichenfolge maskieren müssen.

Wenn Ihr SQL-Dialekt unterstützt CHARINDEX, ist es viel einfacher, ihn stattdessen zu verwenden:

SELECT * FROM MyTable
WHERE CHARINDEX('word1', Column1) > 0
  AND CHARINDEX('word2', Column1) > 0
  AND CHARINDEX('word3', Column1) > 0

Beachten Sie außerdem, dass dies und die Methode in der akzeptierten Antwort nur die Teilzeichenfolgenübereinstimmung und nicht die Wortübereinstimmung abdecken. So zum Beispiel die Zeichenfolge'word1word2word3' würde immer noch übereinstimmen.


1
Dies scheint viel einfacher zu sein, wenn Ihr Suchbegriff eine Variable ist, anstatt vor der Suche die Zeichen '%' hinzufügen zu müssen
ShaneBlake

4
In Microsoft SQL Servern und Engines sollten wir InStr()stattdessen verwendenCHARINDEX
23W

6
@ 23W Es gibt kein InStr in MS SQL
Romano Zumbé

19

Funktion

 CREATE FUNCTION [dbo].[fnSplit] ( @sep CHAR(1), @str VARCHAR(512) )
 RETURNS TABLE AS
 RETURN (
           WITH Pieces(pn, start, stop) AS (
           SELECT 1, 1, CHARINDEX(@sep, @str)
           UNION ALL
           SELECT pn + 1, stop + 1, CHARINDEX(@sep, @str, stop + 1)
           FROM Pieces
           WHERE stop > 0
      )

      SELECT
           pn AS Id,
           SUBSTRING(@str, start, CASE WHEN stop > 0 THEN stop - start ELSE 512 END) AS Data
      FROM
           Pieces
 )

Abfrage

 DECLARE @FilterTable TABLE (Data VARCHAR(512))

 INSERT INTO @FilterTable (Data)
 SELECT DISTINCT S.Data
 FROM fnSplit(' ', 'word1 word2 word3') S -- Contains words

 SELECT DISTINCT
      T.*
 FROM
      MyTable T
      INNER JOIN @FilterTable F1 ON T.Column1 LIKE '%' + F1.Data + '%'
      LEFT JOIN @FilterTable F2 ON T.Column1 NOT LIKE '%' + F2.Data + '%'
 WHERE
      F2.Data IS NULL

2
Ausgezeichnet! Wie fange ich an, etwas über diese Funktion zu lernen, Sir? Was ist Stücke? und kannst du mir Pseudocode über diese Zeile erzählen? SUBSTRING (@str, start, CASE WHEN stop> 0 THEN stop - start ELSE 512 END) AS Data
Khaneddy2013

2
Dieser Schritt war unglaublich ,, Ich bin wirklich JEALOUS :( _______________________________________________________________________________________ INNER JOIN (@FilterTable F1 ON T.Column1 LIKE '%' + F1.Data + '%' LEFT JOIN (@FilterTable F2 ON T.Column1 NOT LIKE '%' + F2.Data + '%'
Ahmad Alkaraki

13

Fügen Sie stattdessen SELECT * FROM MyTable WHERE Column1 CONTAINS 'word1 word2 word3'Und zwischen diesen Wörtern wie:

SELECT * FROM MyTable WHERE Column1 CONTAINS 'word1 And word2 And word3'

Weitere Informationen finden Sie hier https://msdn.microsoft.com/en-us/library/ms187787.aspx

AKTUALISIEREN

Verwenden Sie zum Auswählen von Phrasen doppelte Anführungszeichen wie:

SELECT * FROM MyTable WHERE Column1 CONTAINS '"Phrase one" And word2 And "Phrase Two"'

ps Sie müssen zuerst die Volltextsuche in der Tabelle aktivieren, bevor Sie das Schlüsselwort enthält verwenden. Weitere Informationen finden Sie hier https://docs.microsoft.com/en-us/sql/relational-databases/search/get-started-with-full-text-search


8
SELECT * FROM MyTable WHERE 
Column1 LIKE '%word1%'
AND Column1 LIKE '%word2%'
AND Column1 LIKE  '%word3%'

Geändert ORzu ANDbasierend auf Bearbeiten zu Frage.


Ich brauche alle Wörter, um im Ergebnis in einer beliebigen Kombination enthalten zu sein
Mario M

4

Wenn Sie Oracle Database verwenden , können Sie dies mithilfe der enthaltenen Abfrage erreichen. Enthält Abfragen sind schneller als ähnliche Abfragen.

Wenn Sie alle Wörter brauchen

SELECT * FROM MyTable WHERE CONTAINS(Column1,'word1 and word2 and word3', 1) > 0

Wenn Sie eines der Wörter benötigen

SELECT * FROM MyTable WHERE CONTAINS(Column1,'word1 or word2 or word3', 1) > 0

Enthält einen Bedarfsindex vom Typ CONTEXT für Ihre Spalte.

CREATE INDEX SEARCH_IDX ON MyTable(Column) INDEXTYPE IS CTXSYS.CONTEXT

1
@downvoters Ein Kommentar sagt gerne, was mit der Antwort falsch ist.
Dieselbe

2
OP gibt nicht an, welche Datenbank verwendet wird, und jeder hat angenommen, dass es sich um SQL Server handelt. Aber da Sie Oracle in Ihrer Antwort angegeben haben, verstehe ich Downvoter nicht.
EAmez

4

Wenn Sie nur eine Übereinstimmung finden möchten.

SELECT * FROM MyTable WHERE INSTR('word1 word2 word3',Column1)<>0

SQL Server :

CHARINDEX(Column1, 'word1 word2 word3', 1)<>0

Um eine genaue Übereinstimmung zu erhalten. Beispiel (';a;ab;ac;',';b;')wird keine Übereinstimmung erhalten.

SELECT * FROM MyTable WHERE INSTR(';word1;word2;word3;',';'||Column1||';')<>0

1
'INSTR' ist kein anerkannter integrierter Funktionsname. In meinem SQL Server.
Durgesh Pandey

0

Versuchen Sie, die "Tesarus-Suche" im Volltextindex in MS SQL Server zu verwenden. Dies ist viel besser als die Verwendung von "%" bei der Suche, wenn Sie Millionen von Datensätzen haben. Tesarus haben einen geringen Speicherverbrauch als die anderen. versuche diese Funktionen zu durchsuchen :)


0

Der beste Weg ist, einen Volltextindex für eine Spalte in der Tabelle zu erstellen und statt LIKE enthalten zu verwenden

SELECT * FROM MyTable WHERE 
contains(Column1 , N'word1' )
AND contains(Column1 , N'word2' )
AND contains(Column1 , N'word3' )

0

Warum nicht stattdessen "in" verwenden?

Select *
from table
where columnname in (word1, word2, word3)

2
Weil es nicht funktioniert. Hast du es tatsächlich versucht?
mvp

2
Ich glaube, dies wird nur exakte Übereinstimmungen zurückgeben.
Murray

1
Ich habe auch die ursprüngliche Frage falsch verstanden: Sie wollen keine genaue Übereinstimmung finden, sondern ein Wort, das Teil einer (möglicherweise) größeren Zeichenfolge ist. Für den einfacheren "genau passenden" Fall funktioniert dies, vorausgesetzt, die Wörter stehen zwischen einfachen Anführungszeichen (vgl. SQLfiddle )
sc28

0

Eine der einfachsten Möglichkeiten, um das zu erreichen, was in der Frage erwähnt wird, ist die Verwendung von CONTAINS mit NEAR oder '~'. Zum Beispiel würden die folgenden Abfragen uns alle Spalten geben, die speziell Wort1, Wort2 und Wort3 enthalten.

SELECT * FROM MyTable WHERE CONTAINS(Column1, 'word1 NEAR word2 NEAR word3')

SELECT * FROM MyTable WHERE CONTAINS(Column1, 'word1 ~ word2 ~ word3')

Außerdem gibt CONTAINSTABLE für jedes Dokument einen Rang zurück, der auf der Nähe von "Wort1", "Wort2" und "Wort3" basiert. Wenn ein Dokument beispielsweise den Satz "Das Wort1 ist Wort2 und Wort3" enthält, ist seine Rangfolge hoch, da die Begriffe näher beieinander liegen als in anderen Dokumenten.

Eine andere Sache, die ich hinzufügen möchte, ist, dass wir Proximity_term auch verwenden können, um Spalten zu finden, bei denen sich die Wörter innerhalb eines bestimmten Abstands zwischen ihnen innerhalb der Spaltenphrase befinden.


0

Dies sollte idealerweise mit Hilfe der SQL Server-Volltextsuche erfolgen, wenn Sie verwenden. Wenn Sie dies jedoch aus irgendeinem Grund nicht in Ihrer Datenbank zum Laufen bringen können, finden Sie hier eine leistungsintensive Lösung:

-- table to search in
CREATE TABLE dbo.myTable
    (
    myTableId int NOT NULL IDENTITY (1, 1),
    code varchar(200) NOT NULL, 
    description varchar(200) NOT NULL -- this column contains the values we are going to search in 
    )  ON [PRIMARY]
GO

-- function to split space separated search string into individual words
CREATE FUNCTION [dbo].[fnSplit] (@StringInput nvarchar(max),
@Delimiter nvarchar(1))
RETURNS @OutputTable TABLE (
  id nvarchar(1000)
)
AS
BEGIN
  DECLARE @String nvarchar(100);

  WHILE LEN(@StringInput) > 0
  BEGIN
    SET @String = LEFT(@StringInput, ISNULL(NULLIF(CHARINDEX(@Delimiter, @StringInput) - 1, -1),
    LEN(@StringInput)));
    SET @StringInput = SUBSTRING(@StringInput, ISNULL(NULLIF(CHARINDEX
    (
    @Delimiter, @StringInput
    ),
    0
    ), LEN
    (
    @StringInput)
    )
    + 1, LEN(@StringInput));

    INSERT INTO @OutputTable (id)
      VALUES (@String);
  END;

  RETURN;
END;
GO

-- this is the search script which can be optionally converted to a stored procedure /function


declare @search varchar(max) = 'infection upper acute genito'; -- enter your search string here
-- the searched string above should give rows containing the following
-- infection in upper side with acute genitointestinal tract
-- acute infection in upper teeth
-- acute genitointestinal pain

if (len(trim(@search)) = 0) -- if search string is empty, just return records ordered alphabetically
begin
 select 1 as Priority ,myTableid, code, Description from myTable order by Description 
 return;
end

declare @splitTable Table(
wordRank int Identity(1,1), -- individual words are assinged priority order (in order of occurence/position)
word varchar(200)
)
declare @nonWordTable Table( -- table to trim out auxiliary verbs, prepositions etc. from the search
id varchar(200)
)

insert into @nonWordTable values
('of'),
('with'),
('at'),
('in'),
('for'),
('on'),
('by'),
('like'),
('up'),
('off'),
('near'),
('is'),
('are'),
(','),
(':'),
(';')

insert into @splitTable
select id from dbo.fnSplit(@search,' '); -- this function gives you a table with rows containing all the space separated words of the search like in this e.g., the output will be -
--  id
-------------
-- infection
-- upper
-- acute
-- genito

delete s from @splitTable s join @nonWordTable n  on s.word = n.id; -- trimming out non-words here
declare @countOfSearchStrings int = (select count(word) from @splitTable);  -- count of space separated words for search
declare @highestPriority int = POWER(@countOfSearchStrings,3);

with plainMatches as
(
select myTableid, @highestPriority as Priority from myTable where Description like @search  -- exact matches have highest priority
union                                      
select myTableid, @highestPriority-1 as Priority from myTable where Description like  @search + '%'  -- then with something at the end
union                                      
select myTableid, @highestPriority-2 as Priority from myTable where Description like '%' + @search -- then with something at the beginning
union                                      
select myTableid, @highestPriority-3 as Priority from myTable where Description like '%' + @search + '%' -- then if the word falls somewhere in between
),
splitWordMatches as( -- give each searched word a rank based on its position in the searched string
                     -- and calculate its char index in the field to search
select myTable.myTableid, (@countOfSearchStrings - s.wordRank) as Priority, s.word,
wordIndex = CHARINDEX(s.word, myTable.Description)  from myTable join @splitTable s on myTable.Description like '%'+ s.word + '%'
-- and not exists(select myTableid from plainMatches p where p.myTableId = myTable.myTableId) -- need not look into myTables that have already been found in plainmatches as they are highest ranked
                                                                              -- this one takes a long time though, so commenting it, will have no impact on the result
),
matchingRowsWithAllWords as (
 select myTableid, count(myTableid) as myTableCount from splitWordMatches group by(myTableid) having count(myTableid) = @countOfSearchStrings
)
, -- trim off the CTE here if you don't care about the ordering of words to be considered for priority
wordIndexRatings as( -- reverse the char indexes retrived above so that words occuring earlier have higher weightage
                     -- and then normalize them to sequential values
select s.myTableid, Priority, word, ROW_NUMBER() over (partition by s.myTableid order by wordindex desc) as comparativeWordIndex 
from splitWordMatches s join matchingRowsWithAllWords m on s.myTableId = m.myTableId
)
,
wordIndexSequenceRatings as ( -- need to do this to ensure that if the same set of words from search string is found in two rows,
                              -- their sequence in the field value is taken into account for higher priority
    select w.myTableid, w.word, (w.Priority + w.comparativeWordIndex + coalesce(sequncedPriority ,0)) as Priority
    from wordIndexRatings w left join 
    (
     select w1.myTableid, w1.priority, w1.word, w1.comparativeWordIndex, count(w1.myTableid) as sequncedPriority
     from wordIndexRatings w1 join wordIndexRatings w2 on w1.myTableId = w2.myTableId and w1.Priority > w2.Priority and w1.comparativeWordIndex>w2.comparativeWordIndex
     group by w1.myTableid, w1.priority,w1.word, w1.comparativeWordIndex
    ) 
    sequencedPriority on w.myTableId = sequencedPriority.myTableId and w.Priority = sequencedPriority.Priority
),
prioritizedSplitWordMatches as ( -- this calculates the cumulative priority for a field value
select  w1.myTableId, sum(w1.Priority) as OverallPriority from wordIndexSequenceRatings w1 join wordIndexSequenceRatings w2 on w1.myTableId =  w2.myTableId 
where w1.word <> w2.word group by w1.myTableid 
),
completeSet as (
select myTableid, priority from plainMatches -- get plain matches which should be highest ranked
union
select myTableid, OverallPriority as priority from prioritizedSplitWordMatches -- get ranked split word matches (which are ordered based on word rank in search string and sequence)
),
maximizedCompleteSet as( -- set the priority of a field value = maximum priority for that field value
select myTableid, max(priority) as Priority  from completeSet group by myTableId
)
select priority, myTable.myTableid , code, Description from maximizedCompleteSet m join myTable  on m.myTableId = myTable.myTableId 
order by Priority desc, Description -- order by priority desc to get highest rated items on top
--offset 0 rows fetch next 50 rows only -- optional paging

-2
SELECT * FROM MyTable WHERE Column1 Like "*word*"

Dadurch werden alle Datensätze angezeigt, in denen column1ein Teilwert enthalten ist word.


-2
DECLARE @SearchStr nvarchar(100)
SET @SearchStr = ' '



CREATE TABLE #Results (ColumnName nvarchar(370), ColumnValue nvarchar(3630))

SET NOCOUNT ON

DECLARE @TableName nvarchar(256), @ColumnName nvarchar(128), @SearchStr2 nvarchar(110)
SET  @TableName = ''
SET @SearchStr2 = QUOTENAME('%' + @SearchStr + '%','''')

WHILE @TableName IS NOT NULL

BEGIN
    SET @ColumnName = ''
    SET @TableName = 
    (
        SELECT MIN(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME))
        FROM     INFORMATION_SCHEMA.TABLES
        WHERE         TABLE_TYPE = 'BASE TABLE'
            AND    QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) > @TableName
            AND    OBJECTPROPERTY(
                    OBJECT_ID(
                        QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)
                         ), 'IsMSShipped'
                           ) = 0
    )

    WHILE (@TableName IS NOT NULL) AND (@ColumnName IS NOT NULL)

    BEGIN
        SET @ColumnName =
        (
            SELECT MIN(QUOTENAME(COLUMN_NAME))
            FROM     INFORMATION_SCHEMA.COLUMNS
            WHERE         TABLE_SCHEMA    = PARSENAME(@TableName, 2)
                AND    TABLE_NAME    = PARSENAME(@TableName, 1)
                AND    DATA_TYPE IN ('char', 'varchar', 'nchar', 'nvarchar', 'int', 'decimal')
                AND    QUOTENAME(COLUMN_NAME) > @ColumnName
        )

        IF @ColumnName IS NOT NULL

        BEGIN
            INSERT INTO #Results
            EXEC
            (
                'SELECT ''' + @TableName + '.' + @ColumnName + ''', LEFT(' + @ColumnName + ', 3630) FROM ' + @TableName + ' (NOLOCK) ' +
                ' WHERE ' + @ColumnName + ' LIKE ' + @SearchStr2
            )
        END
    END   
END

SELECT ColumnName, ColumnValue FROM #Results

DROP TABLE #Results

2
Vielen Dank für dieses Code-Snippet, das möglicherweise nur begrenzte, sofortige Hilfe bietet. Eine richtige Erklärung würde ihren langfristigen Wert erheblich verbessern, indem sie zeigt, warum dies eine gute Lösung für das Problem ist, und es für zukünftige Leser mit anderen, ähnlichen Fragen nützlicher machen. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, einschließlich der von Ihnen getroffenen Annahmen.
Mogsdad

-5
select * from table where name regexp '^word[1-3]$'

oder

select * from table where name in ('word1','word2','word3')

3
Ist "regexp" Standard-SQL?
Peter Mortensen

2
Sollte das Wort bei der zweiten Abfrage nicht zitiert werden?
Peter Mortensen

1
Dieser Code scheint zu überprüfen , ob die Spalte gleich einer der drei Worte. Die Frage ist , über die Überprüfung , ob die Spalte enthält alle der drei Wörter.
Sam

7
Hiya, das könnte das Problem lösen ... aber es wäre gut, wenn Sie Ihre Antwort bearbeiten und eine kleine Erklärung darüber geben könnten , wie und warum es funktioniert :) Vergessen Sie nicht - es gibt jede Menge Neulinge im Stack-Überlauf, und sie könnten ein oder zwei Dinge aus Ihrem Fachwissen lernen - was für Sie offensichtlich ist, ist für sie möglicherweise nicht so.
Taryn East
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.