Поле SQL SELECT WHERE містить слова


562

Мені потрібен вибір, який би повертав такі результати:

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

І мені потрібні всі результати, тобто це включає рядки з 'word2 word3 word1' або 'word1 word3 word2' або будь-яку іншу комбінацію цих трьох.

Усі слова повинні бути в результаті.

Відповіді:


843

Досить повільний, але робочий спосіб включати будь-яке слово:

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

Якщо вам потрібні всі слова, використовуйте це:

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

Якщо ви хочете чогось швидшого, вам потрібно вивчити повнотекстовий пошук, і це дуже специфічно для кожного типу бази даних.


3
+ 1 Я згоден, що це повільніше, але його можна пом'якшити за допомогою хорошої індексації
Преет Сангха

12
@PreetSangha Індексація, коли ви шукаєте LIKE, починаючи з підстановки? Покажіть, будь ласка, як!
Popnoodles

1
У PostgreSQL 9.1 і пізніших версіях ви можете створити індекс триграми, який може індексувати такі пошуки .
mvp

2
@AquaAlex: ваш текст вийде з ладу, якщо текст є word3 word2 word1.
mvp

3
Ще один недолік цього підходу: "% word%" також знайде "слова", "кросворд" та "меч" (лише як приклад). Мені доведеться зробити стовпчик1 СЛОВО "Слово" АБО стовпець1 НЕОБХІДНО "Слово%" АБО стовпець1 ПОБАВЛЕННЯ "% слово" АБО стовпець1 НЕОБХІДНО "слово", щоб просто знайти точні збіги слів - і воно все одно не вдасться до записів, де слова не є просто розділені пробілами.
BlaM

81

Зауважте, що якщо ви використовуєте LIKEдля визначення того, чи є рядок підрядкою іншої рядки, вам слід уникнути символів, що відповідають шаблону у вашій рядку пошуку.

Якщо ваш діалект SQL підтримує CHARINDEX, використовувати його набагато простіше:

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

Також майте на увазі, що це та метод у прийнятій відповіді охоплюють лише відповідність підрядків, а не відповідність слів. Так, наприклад, рядок 'word1word2word3'все одно збігався б.


1
Це здається набагато простішим, якщо пошуковий термін є змінною, а не додавати символи '%' перед пошуком
ShaneBlake

4
На серверах та двигунах Microsoft SQL ми повинні використовувати InStr()замість цьогоCHARINDEX
Вт

6
@ 23W В MS SQL немає InStr
Романо Зумбе

19

Функція

 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
 )

Запит

 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
Чудовий! Як почати дізнаватися про цю функцію, сер? що таке Шматки? і чи можете ви сказати мені псевдокод про цей рядок? ПІДПРИЄМСТВО (@str, старт, СЛУЧАЙ КОЛИ зупинка> 0 ЦЕ зупинка - запуск ELSE 512 END) AS Data
Khaneddy2013

2
Цей крок був неймовірним ,, Я дійсно РЕАЛЬНИЙ :( _______________________________________________________________________________________ ВНУТРІШНЕ ПРИЄДНАЙТЕСЬ (@FilterTable F1 ON T.Column1 LIKE '%' + F1.Data + '%' LEFT JOHINE) + F2.Data + '%'
Ахмад Алкаракі

13

Замість цього SELECT * FROM MyTable WHERE Column1 CONTAINS 'word1 word2 word3'додайте І між такими словами, як:

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

Детальніше дивіться тут https://msdn.microsoft.com/en-us/library/ms187787.aspx

ОНОВЛЕННЯ

Для вибору фраз використовуйте подвійні лапки, як-от:

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

ps Ви повинні спочатку ввімкнути повний пошук тексту на таблиці, перш ніж використовувати ключове слово. Детальніше дивіться тут 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%'

Змінено ORна ANDоснові редагування до питання.


Мені потрібно, щоб усі слова містилися в результаті в будь-якій комбінації
Маріо М

4

Якщо ви використовуєте базу даних Oracle, ви можете досягти цього, використовуючи запит, що містить . Містить запити швидше, ніж як запит.

Якщо вам потрібні всі слова

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

Якщо вам потрібне якесь із слів

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

Містить потрібний індекс типу CONTEXT у колонці.

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

1
@downvoters Вдячний коментар, який розповідає про те, що не так у відповіді. Цей самий запит працює в нашому корпоративному рішенні більше 1000 разів на день, без жодних питань :)
mirmdasif

2
OP не визначає, яку базу даних використовує, і всі припустили, що це Sql Server. Але так як ви вказали Oracle у своїй відповіді, я не розумію низку.
EAmez

4

Якщо ви просто хочете знайти відповідність.

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

SQL Server:

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

Щоб отримати точну відповідність. Приклад (';a;ab;ac;',';b;')не отримає збіг.

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

1
'INSTR' - це не розпізнана вбудована назва функції. У моєму SQL Server.
Дургеш Пандей

0

спробуйте використовувати "пошук tesarus" у повнотекстовому покажчику в MS SQL Server. Це набагато краще, ніж використовувати "%" у пошуку, якщо у вас мільйони записів. Тезар має менший обсяг пам'яті, ніж інші. спробуйте шукати ці функції :)


0

найкращим способом є створення повнотекстового індексу на стовпчик таблиці та використання містять замість LIKE

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

0

чому б не використати натомість "в"?

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

2
Тому що це не працює. Ви насправді пробували?
mvp

2
Я вірю, що це поверне лише точні збіги.
Мюррей

1
Я також неправильно зрозумів початкове запитання: вони не хочуть знайти точну відповідність, але слово є частиною (можливо) більшої рядки. Для більш простого випадку "точно збігається", це працює за умови, що слова знаходяться між цитатами (пор. SQLfiddle )
sc28

0

Один з найпростіших способів домогтися того, що згадується у питанні, - використовувати CONTAINS with NEAR або '~'. Наприклад, наступні запити дадуть нам усі стовпці, які спеціально включають word1, word2 та word3.

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

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

Крім того, CONTAINSTABLE повертає рейтинг для кожного документа на основі близькості "word1", "word2" та "word3". Наприклад, якщо документ містить речення "Слово1 - це слово2 і Слово3", його ранжування буде високим, оскільки терміни ближче один до одного, ніж в інших документах.

Ще одне, що я хотів би додати, це те, що ми також можемо використовувати proximity_term для пошуку стовпців, де слова знаходяться на певній відстані між ними всередині фрази стовпця.


0

Це в ідеалі слід робити за допомогою повнотекстового пошуку сервера sql, якщо використовується. Однак, якщо ви не можете зробити це, працюючи над вашою БД, з якихось причин, ось рішення, яке вимагає високої продуктивності:

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

Це відобразить усі записи, де column1міститься часткове значення 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
Дякуємо за цей фрагмент коду, який може надати деяку обмежену, негайну допомогу. Належне пояснення було б значно поліпшити свою довгострокову цінність , показуючи , чому це хороше рішення проблеми, і зробить його більш корисним для читачів майбутніх з іншими подібними питаннями. Будь ласка , змініть свій відповідь , щоб додати деякі пояснення, в тому числі припущень , які ви зробили.
Могсдад

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

або

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

3
Є "regexp" стандартним SQL?
Пітер Мортенсен

2
У другому запиті не слід цитувати слово?
Пітер Мортенсен

1
Цей код, схоже, перевіряє, чи стовпець дорівнює одному з трьох слів. Питання полягає в тому, щоб перевірити, чи містить стовпець усі три слова.
Сем,

7
Привіт, це цілком може вирішити проблему ... але було б добре, якби ви могли відредагувати свою відповідь та дати невелике пояснення про те, як і чому це працює :) Не забувайте - на купі переповнення Stack є купа новачків, і вони могли б дізнатися щось чи дві з вашої експертизи - те, що вам очевидно, може бути не таким для них.
Taryn East
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.