Bidang SQL SELECT WHERE berisi kata-kata

562

Saya perlu pilih yang akan mengembalikan hasil seperti ini:

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

Dan saya membutuhkan semua hasil, yaitu ini termasuk string dengan 'word2 word3 word1' atau 'word1 word3 word2' atau kombinasi lainnya dari ketiganya.

Semua kata harus ada dalam hasil.

Mario M
sumber

Jawaban:

844

Agak lambat, tetapi metode yang bekerja untuk memasukkan setiap kata-kata:

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

Jika Anda membutuhkan semua kata untuk hadir, gunakan ini:

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

Jika Anda menginginkan sesuatu yang lebih cepat, Anda perlu melihat ke dalam pencarian teks lengkap, dan ini sangat spesifik untuk setiap jenis database.

mvp
sumber
3
+1 Saya setuju ini lebih lambat tapi ini bisa dikurangi dengan pengindeksan yang baik
Preet Sangha
12
@PreetSangha Pengindeksan ketika Anda mencari SEPERTI dimulai dengan kartu liar? Tolong tunjukkan saya bagaimana!
Popnoodles
1
Di PostgreSQL 9.1 dan yang lebih baru, Anda dapat membuat indeks trigram yang dapat mengindeks pencarian tersebut .
mvp
2
@ Aquaqua: pernyataan Anda akan gagal jika teks memiliki word3 word2 word1.
mvp
3
Kelemahan lain dari pendekatan ini: '% word%' juga akan menemukan 'kata-kata', 'crosswordpuzzle' dan 'sword' (hanya sebagai contoh). Saya harus melakukan kolom1 SUKA 'kata' ATAU kolom1 SUKA 'kata%' ATAU kolom1 SUKA '% kata' ATAU kolom1 SUKA 'hanya untuk menemukan kata yang cocok persis - dan itu akan tetap gagal untuk entri di mana kata-kata tidak hanya dipisahkan dengan spasi.
BlaM
81

Perhatikan bahwa jika Anda menggunakan LIKEuntuk menentukan apakah string adalah substring dari string lain, Anda harus melarikan diri dari karakter yang cocok dengan pola dalam string pencarian Anda.

Jika dialek SQL Anda mendukung CHARINDEX, lebih mudah untuk menggunakannya:

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

Juga, harap diingat bahwa ini dan metode dalam jawaban yang diterima hanya mencakup pencocokan substring daripada pencocokan kata. Jadi, misalnya, senar 'word1word2word3'masih cocok.

Sam
sumber
1
Ini tampaknya jauh lebih mudah jika istilah pencarian Anda adalah variabel daripada harus menambahkan karakter '%' sebelum mencari
ShaneBlake
4
Dalam Microsoft SQL server dan mesin kami harus menggunakan InStr()sebaliknyaCHARINDEX
23W
6
@ 23W Tidak ada InStr di MS SQL
Romano Zumbé
19

Fungsi

 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
 )

Pertanyaan

 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
Eduardo Cuomo
sumber
2
Luar biasa! Bagaimana cara mulai belajar tentang fungsi ini, Pak? apa itu Potongan? dan bisakah Anda memberi tahu saya kodesemu tentang baris ini? SUBSTRING (@str, mulai, KASUS KETIKA berhenti> 0 KEMUDIAN berhenti - mulai ELSE 512 AKHIR) SEBAGAI Data
Khaneddy2013
2
Langkah ini luar biasa ,, Saya SANGAT MENCINTAI :( _______________________________________________________________________________________ INNER JOIN (@FilterTable F1 ON T.Column1 SUKA '%' + F1.Data + '%' LEFT JOIN (@FilterTable F2 ON T.Column1 TIDAK SUKA%% ') + F2.Data + '%'
Ahmad Alkaraki
13

Alih-alih SELECT * FROM MyTable WHERE Column1 CONTAINS 'word1 word2 word3', tambahkan Dan di antara kata-kata seperti:

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

untuk detail, lihat di sini https://msdn.microsoft.com/en-us/library/ms187787.aspx

MEMPERBARUI

Untuk memilih frasa, gunakan tanda kutip ganda seperti:

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

ps Anda harus terlebih dahulu mengaktifkan Pencarian Teks Lengkap di atas meja sebelum menggunakan berisi kata kunci. untuk lebih jelasnya, lihat di sini https://docs.microsoft.com/en-us/sql/relational-databases/search/get-started-with-full-text-search

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

Diubah ORmenjadi ANDberdasarkan edit ke pertanyaan.

Jon Crowell
sumber
Saya membutuhkan semua kata untuk terkandung dalam hasil dalam kombinasi apa pun
Mario M
4

Jika Anda menggunakan Oracle Database maka Anda dapat mencapai ini menggunakan mengandung query. Berisi querys lebih cepat daripada query seperti.

Jika Anda membutuhkan semua kata

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

Jika Anda membutuhkan kata-kata

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

Berisi indeks kebutuhan bertipe CONTEXT pada kolom Anda.

CREATE INDEX SEARCH_IDX ON MyTable(Column) INDEXTYPE IS CTXSYS.CONTEXT
mirmdasif
sumber
1
@ downvoters Suatu komentar dihargai dengan mengatakan apa yang salah dengan jawabannya. Permintaan yang sama ini berjalan dalam solusi perusahaan kami lebih dari 1000 kali per hari, tanpa masalah :)
mirmdasif
2
OP tidak menentukan basis data mana yang digunakan dan semua orang berasumsi bahwa itu adalah Sql Server. Tetapi karena Anda telah menentukan Oracle dalam respons Anda, saya tidak mengerti downvoter.
EAmez
4

Jika Anda hanya ingin menemukan kecocokan.

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

SQL Server:

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

Untuk mendapatkan kecocokan yang tepat. Contoh (';a;ab;ac;',';b;')tidak akan mendapatkan kecocokan.

SELECT * FROM MyTable WHERE INSTR(';word1;word2;word3;',';'||Column1||';')<>0
Joshua Balan
sumber
1
'INSTR' bukan nama fungsi bawaan yang dikenali. Di SQL Server saya.
Durgesh Pandey
0

coba gunakan "pencarian tesarus" dalam indeks teks lengkap di MS SQL Server. Ini jauh lebih baik daripada menggunakan "%" dalam pencarian jika Anda memiliki jutaan catatan. tesarus memiliki sejumlah kecil konsumsi memori dibandingkan yang lain. coba cari fungsi ini :)

Daryl Arenas
sumber
0

Cara terbaik adalah membuat indeks teks lengkap pada kolom dalam tabel dan menggunakan isi daripada LIKE

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

mengapa tidak menggunakan "in" saja?

Select *
from table
where columnname in (word1, word2, word3)
Michael Angerbauer
sumber
2
Karena itu tidak berhasil. Sudahkah Anda mencobanya?
mvp
2
Saya yakin ini hanya akan mengembalikan kecocokan persis.
Murray
1
Saya juga salah memahami pertanyaan awal: mereka tidak ingin menemukan pasangan yang tepat, tetapi sebuah kata yang menjadi bagian dari string (mungkin) yang lebih besar. Untuk kasus "pencocokan tepat" yang lebih sederhana, ini berfungsi asalkan kata-kata tersebut berada di antara tanda kutip tunggal (lih. SQLfiddle )
sc28
0

Salah satu cara termudah untuk mencapai apa yang disebutkan dalam pertanyaan adalah dengan menggunakan WADAH dengan DEKAT atau '~'. Misalnya, pertanyaan berikut akan memberi kita semua kolom yang secara khusus mencakup kata1, kata2 dan kata3.

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

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

Selain itu, CONTAINSTABLE mengembalikan peringkat untuk setiap dokumen berdasarkan kedekatan "word1", "word2" dan "word3". Misalnya, jika sebuah dokumen berisi kalimat, "Kata1 adalah kata2 dan kata3," peringkatnya akan tinggi karena ketentuannya lebih dekat satu sama lain daripada di dokumen lain.

Satu hal lain yang ingin saya tambahkan adalah bahwa kita juga dapat menggunakan proximity_term untuk menemukan kolom di mana kata-kata berada di dalam jarak tertentu di antara mereka di dalam frase kolom.

Anastasios Selmanis
sumber
0

Ini idealnya harus dilakukan dengan bantuan pencarian teks lengkap sql server jika menggunakan. Namun, jika Anda tidak bisa mengerjakannya dengan DB karena suatu alasan, berikut ini adalah solusi intensif kinerja: -

-- 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
JBelfort
sumber
-2
SELECT * FROM MyTable WHERE Column1 Like "*word*"

Ini akan menampilkan semua catatan yang column1berisi nilai parsial word.

Jino
sumber
-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
pengguna2274887
sumber
2
Terima kasih atas cuplikan kode ini, yang mungkin memberikan bantuan terbatas dan segera. Sebuah penjelasan yang tepat akan sangat meningkatkan nilai jangka panjang dengan menunjukkan mengapa ini adalah solusi yang baik untuk masalah ini, dan akan membuatnya lebih bermanfaat untuk pembaca masa depan dengan lainnya, pertanyaan-pertanyaan serupa. Harap edit jawaban Anda untuk menambahkan beberapa penjelasan, termasuk asumsi yang Anda buat.
Mogsdad
-5
select * from table where name regexp '^word[1-3]$'

atau

select * from table where name in ('word1','word2','word3')
vidyadhar
sumber
3
Apakah "regexp" SQL standar?
Peter Mortensen
2
Untuk permintaan kedua, bukankah seharusnya kata tersebut dikutip?
Peter Mortensen
1
Kode ini tampaknya memeriksa apakah kolom sama dengan satu dari tiga kata. Pertanyaannya adalah tentang memeriksa jika kolom berisi semua dari tiga kata.
Sam
7
Hai, ini mungkin bisa menyelesaikan masalah ... tetapi akan lebih baik jika Anda dapat mengedit jawaban Anda dan memberikan sedikit penjelasan tentang bagaimana dan mengapa ia bekerja :) Jangan lupa - ada banyak pemula di Stack overflow, dan mereka bisa belajar satu atau dua dari keahlian Anda - yang jelas bagi Anda mungkin tidak demikian bagi mereka.
Taryn East