SQL SELECT NEREDE alanı kelimeler içeriyor


562

Bunun gibi sonuçlar döndürecek bir seçime ihtiyacım var:

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

Ve tüm sonuçlara ihtiyacım var, yani bu 'word2 word3 word1' veya 'word1 word3 word2' veya üçün herhangi bir kombinasyonunu içeren dizeleri içerir.

Tüm kelimelerin sonuçta olması gerekir.

Yanıtlar:


843

Kelimelerin herhangi birini eklemek için oldukça yavaş, ama çalışma yöntemi :

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

Eğer gerekirse tüm mevcut olması kelimeler şunu kullanın:

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

Daha hızlı bir şey istiyorsanız, tam metin aramasına bakmanız gerekir ve bu her veritabanı türü için çok belirgindir.


3
+ 1 Daha yavaş olduğuna katılıyorum ama iyi endeksleme ile hafifletilebilir
Preet Sangha

12
@PreetSangha Joker ile başlayan LIKE ararken indeksleme? Lütfen bana nasıl olduğunu göster!
Popnoodles

1
PostgreSQL 9.1 ve sonraki sürümlerde, bu tür aramaları dizine ekleyebilen trigram dizini oluşturabilirsiniz .
mvp

2
@AquaAlex: metin varsa ifadeniz başarısız olur word3 word2 word1.
mvp

3
Bu yaklaşımın bir başka dezavantajı: '% word%' ayrıca 'kelimeler', 'çapraz bulmaca' ve 'kılıç' (örnek olarak) bulacak. Ben tam kelime eşleşmelerini bulmak için bir sütun1 GİBİ 'kelime' VEYA sütun1 GİBİ 'kelime%' VEYA sütun1 GİBİ '% kelime' VEYA sütun1 GİBİ 'kelime' yapmak zorundayım ve kelimelerin olmadığı girişler için hala başarısız olur boşluklarla ayrılmış.
BlaM

81

LIKEBir dizenin başka bir dizenin alt dizesi olup olmadığını belirlemek için kullanırsanız , arama dizenizdeki desen eşleme karakterlerinden kaçmanız gerektiğini unutmayın.

SQL lehçeniz destekliyorsa CHARINDEX, onu kullanmak çok daha kolaydır:

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

Ayrıca, bunun ve kabul edilen yanıttaki yöntemin kelime eşleştirme yerine yalnızca alt dize eşleşmesini kapsadığını unutmayın. Örneğin, dize 'word1word2word3'yine de eşleşir.


1
Arama teriminiz aramadan önce '%' karakterlerini eklemek yerine bir değişkense bu çok daha kolay görünüyor
ShaneBlake

4
Microsoft SQL sunucuları ve motorlarında InStr()bunun yerine kullanmalıyızCHARINDEX
23W

6
@ 23W MS SQL'de
InStr

19

fonksiyon

 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
 )

Sorgu

 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
Exellent! Bu işlevi öğrenmeye nasıl başlayabilirsiniz, efendim? Parçalar nedir? ve bana bu hat hakkında yalancı kod söyleyebilir misin? SUBSTRING (@str, start, durduğunda DURUM> 0 SONRA durdur - ELSE 512
END'i başlat

2
Bu hamle inanılmazdı, ben gerçekten JEALOUS'um :( _______________________________________________________________________________________ INNER JOIN (@Column1 GİBİ FİLTRE1 F2 '%' + F1.Data + '%' LEFT JOIN (@FiltTable F2 T.Column1 GİBİ DEĞİL '%' + F2.Data + '%'
Ahmad Alkaraki

13

Bunun yerine, SELECT * FROM MyTable WHERE Column1 CONTAINS 'word1 word2 word3'gibi kelimeler arasına And ekleyin:

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

ayrıntılar için buraya bakın https://msdn.microsoft.com/en-us/library/ms187787.aspx

GÜNCELLEME

Cümleleri seçmek için aşağıdaki gibi çift tırnak kullanın:

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

ps , include anahtar sözcüğünü kullanmadan önce tabloda Tam Metin Arama'yı etkinleştirmeniz gerekir. Daha fazla ayrıntı için buraya bakın 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%'

Değişti ORiçin ANDsoruya düzenlemek dayalı.


Herhangi bir kombinasyonda sonuçta yer almak için tüm kelimelere ihtiyacım var
Mario M

4

Eğer kullanıyorsanız , Oracle Veritabanı ardından kullanmakta bunu başarabilirsiniz içeren sorgu. Sorguları sorgudan daha hızlı içerir.

Tüm kelimelere ihtiyacınız varsa

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

Kelimelerin herhangi birine ihtiyacınız varsa

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

Sütununuzda CONTEXT türünde bir gereksinim dizini içerir .

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

1
@downvoters Cevabı neyin yanlış olduğunu söyleyen bir yorum için teşekkür ederiz. Aynı sorgu, kurumsal çözümümüzde günde 1000'den fazla kez, herhangi bir sorun olmadan çalışıyor :)
mirmdasif

2
OP hangi veritabanının kullanıldığını belirtmez ve herkes Sql Server olduğunu varsaymıştır. Ama cevabınızda Oracle'ı belirttiğiniz için downvoters'ı anlamıyorum.
EAmez

4

Sadece bir eşleşme bulmak istiyorsan.

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

SQL Server :

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

Tam eşleşme elde etmek için. Örnek (';a;ab;ac;',';b;')bir eşleşme elde etmeyecek.

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

1
'INSTR' tanınmış bir yerleşik işlev adı değil. SQL Server'ımda.
Durgesh Pandey

0

MS SQL Server'da tam metin dizini "tesarus arama" kullanmayı deneyin. Milyonlarca kaydınız varsa aramada "%" kullanmaktan daha iyidir. tesarusun diğerlerinden daha az miktarda bellek tüketimi vardır. bu işlevleri aramayı deneyin :)


0

en iyi yolu tablodaki bir sütun üzerinde tam metin dizini yapmak ve LIKE yerine içeren kullanın

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

0

neden yerine "in" kullanmıyorsunuz?

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

2
Çünkü işe yaramıyor. Gerçekten denedin mi?
mvp

2
Bunun sadece tam eşleşmeleri getireceğine inanıyorum.
Murray

1
Ayrıca orijinal soruyu yanlış anladım: kesin bir eşleşme bulmak istemiyorlar, ancak bir kelime (muhtemelen) daha büyük bir dizenin parçası. Daha basit "tam eşleme" davası için, bu kelimeler tek tırnak arasında (cf. SQLfiddle )
sc28

0

Soruda belirtilenlere ulaşmanın en kolay yollarından biri, KONTAKLAR'ı YAKIN veya '~' ile kullanmaktır. Örneğin, aşağıdaki sorgular bize özellikle word1, word2 ve word3 içeren tüm sütunları verecektir.

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

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

Buna ek olarak, CONTAINSTABLE "word1", "word2" ve "word3" in yakınlığına dayalı olarak her belge için bir sıralama döndürür. Örneğin, bir belge "word1 word2 ve word3" cümlesini içeriyorsa, terimleri diğer belgelerdekinden daha yakın olduğu için sıralaması yüksek olacaktır.

Eklemek istediğim bir diğer şey, aynı zamanda, kelimelerin sütun deyimi içinde aralarındaki belirli bir mesafe içinde olduğu sütunları bulmak için proximity_term'i kullanabilmemizdir.


0

Bu ideal olarak sql server tam metin arama yardımı ile yapılmalıdır. Ancak, herhangi bir nedenle DB'nizde çalışmasını sağlayamıyorsanız, işte performans yoğun bir çözüm: -

-- 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*"

Bu, column1kısmi değerin bulunduğu tüm kayıtları görüntüler 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
Sınırlı ve anında yardım sağlayabilecek bu kod snippet'i için teşekkür ederiz. Bir Doğru bir açıklama ölçüde uzun vadeli değer artıracak göstererek neden bu soruna iyi bir çözüm olduğunu ve diğer benzer sorularla gelecek okuyucularına daha kullanışlı bir hale getirecektir. Yaptığınız varsayımlar dahil bazı açıklamalar eklemek için lütfen yanıtınızı düzenleyin .
Mogsdad

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

veya

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

3
"Normal ifade" standart SQL midir?
Peter Mortensen

2
İkinci sorgu için, kelime alıntılanmamalı mı?
Peter Mortensen

1
Bu kod, sütunun üç kelimeden birine eşit olup olmadığını kontrol ediyor gibi görünüyor . Soru, sütunun üç kelimenin tümünü içerip içermediğini kontrol etmektir .
Sam

7
Hiya, bu sorunu iyi çözebilir ... ama cevabınızı düzenleyebilir ve nasıl ve neden işe yaradığına dair biraz açıklama yapabilirseniz iyi olur :) Unutmayın - Stack overflow'da yeni başlayanlar var, ve uzmanlığınızdan bir iki şey öğrenebilirler - sizin için açık olan şey onlar için böyle olmayabilir.
Taryn East
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.