2015-06-22 3 views
3

Я создаю модуль формулировки ключевых слов, где я хочу искать данные, используя слова, разделенные запятой. И поиск классифицируется как запятая , и минус -. Я знаю, что механизм реляционной базы данных разработан по принципу, что ячейка содержит одно значение и подчиняется этому правилу, может помочь в производительности. Но в этом случае таблица уже работает и имеет миллионы данных и не может изменять структуру таблицы ,SQL Индивидуальный поиск со специальными символами

Посмотрите на пример того, что я именно хочу сделать

У меня есть название основной таблицы tbl_main в SQL

AS_ID KWD 
1 Man,Businessman,Business,Office,confidence,arms crossed 
2 Man,Businessman,Business,Office,laptop,corridor,waiting 
3 man,business,mobile phone,mobile,phone 
4 Welcome,girl,Greeting,beautiful,bride,celebration,wedding,woman,happiness 
5 beautiful,bride,wedding,woman,girl,happiness,mobile phone,talking 
6 woman,girl,Digital Tablet,working,sitting,online 
7 woman,girl,Digital Tablet,working,smiling,happiness,hand on chin 

Если поиск текста = Мужчина, бизнесмен, то результат AS_ID есть = 1,2

Если поиск текста = Человек, -Businessman, то результат AS_ID есть = 3

I е поиски текст = женщина, девушка, -Работа то результат AS_ID есть = 4,5

Если поиск текст = женщина, девушка, то результат AS_ID есть = 4,5,6,7

Что лучше, почему делать это, помощь очень было оценено заранее

+0

Похож на задачу для [Полнотекстового поиска] (https://msdn.microsoft.com/en-us/library/ms142571%28v=sql.105%29.aspx) – cha

+1

Не могли бы вы подробнее рассказать объяснения в каждом из ваших примеров? –

+0

@wewesthemenace Да! Конечно, обновление будет за 5 минут. – Gitz

ответ

2

Вот моя попытка с помощью Джеффа Moden-х DelimitedSplit8k разделить значения, разделенные запятой.

Во-первых, здесь есть функция разветвитель (проверьте статью для обновления скрипта):

CREATE FUNCTION [dbo].[DelimitedSplit8K](
    @pString VARCHAR(8000), @pDelimiter CHAR(1) 
) 
RETURNS TABLE WITH SCHEMABINDING AS 
RETURN 
WITH E1(N) AS (
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 
) 
,E2(N) AS (SELECT 1 FROM E1 a, E1 b) 
,E4(N) AS (SELECT 1 FROM E2 a, E2 b) 
,cteTally(N) AS(
    SELECT TOP (ISNULL(DATALENGTH(@pString), 0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4 
) 
,cteStart(N1) AS(
    SELECT 1 UNION ALL 
    SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString, t.N, 1) = @pDelimiter 
), 
cteLen(N1, L1) AS(
SELECT 
    s.N1, 
    ISNULL(NULLIF(CHARINDEX(@pDelimiter, @pString, s.N1),0) - s.N1, 8000) 
FROM cteStart s 
) 
SELECT 
    ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1), 
    Item  = SUBSTRING(@pString, l.N1, l.L1) 
FROM cteLen l 

Вот полное решение:

-- search parameter 
DECLARE @search_text VARCHAR(8000) = 'woman,girl,-working' 

-- split comma-separated search parameters 
-- items starting in '-' will have a value of 1 for exclude 
DECLARE @search_values TABLE(ItemNumber INT, Item VARCHAR(8000), Exclude BIT) 
INSERT INTO @search_values 
SELECT 
    ItemNumber, 
    CASE WHEN LTRIM(RTRIM(Item)) LIKE '-%' THEN LTRIM(RTRIM(STUFF(Item, 1, 1 ,''))) ELSE LTRIM(RTRIM(Item)) END, 
    CASE WHEN LTRIM(RTRIM(Item)) LIKE '-%' THEN 1 ELSE 0 END 
FROM dbo.DelimitedSplit8K(@search_text, ',') s 

;WITH CteSplitted AS(-- split each KWD to separate rows 
    SELECT * 
    FROM tbl_main t 
    CROSS APPLY(
     SELECT 
      ItemNumber, Item = LTRIM(RTRIM(Item)) 
     FROM dbo.DelimitedSplit8K(t.KWD, ',') 
    )x 
) 
SELECT 
    cs.AS_ID 
FROM CteSplitted cs 
INNER JOIN @search_values sv 
    ON sv.Item = cs.Item 
GROUP BY cs.AS_ID 
HAVING 
    -- all parameters should be included (Relational Division with no Remainder) 
    COUNT(DISTINCT cs.Item) = (SELECT COUNT(DISTINCT Item) FROM @search_values WHERE Exclude = 0) 
    -- no exclude parameters 
    AND SUM(CASE WHEN sv.Exclude = 1 THEN 1 ELSE 0 END) = 0 

SQL Fiddle

Это используется решение от Реляционное подразделение без остатка проблема обсуждалась в этом article Dwain Camps.

+0

Wow !! Это то, что я ищу: D большое спасибо вам. Очень ценю ваш ответ :) @wewesthemenace – Gitz

+0

Нет проблем , Это был отличный опыт обучения. знак равно –

1

Из того, что вы описали, вы хотите, ключевые слова, которые включены в поиска текста быть матч в KWD колонке, и те, которые имеют префикс -, которые должны быть исключены.

Несмотря на данные, существующие в этом формате, все еще имеет смысл нормализовать данные, а затем запросить на основе наличия или отсутствия ключевых слов.

Чтобы сделать это, очень грубо говоря: -

  1. Создание двух дополнительных таблиц - Keyword и tbl_Main_Keyword. Keyword содержит отдельный список каждого из возможных ключевых слов, а tbl_Main_Keyword содержит ссылку между каждой записью в tbl_Main каждой записи Keyword, где есть совпадение. Обеспечьте создание индекса в текстовом поле для ключевого слова (например, столбца Keyword.KeywordText или того, что вы его называете), а также поля KeywordID в таблице tbl_Main_Keyword. Создайте внешние ключи между таблицами.
  2. Напишите DML (или используйте отдельную программу, такую ​​как программа на C#), чтобы перебирать каждую запись, анализировать текст и вставлять каждое отдельное ключевое слово, встречающееся в таблицу Keyword. Создайте отношение к строке для каждого ключевого слова в записи tbl_main.
  3. Теперь, для поиска, разобрать на текст для поиска в ключевые слова и составить запрос к tbl_Main_Keyword таблице, содержащей как WHERE KeywordID IN и WHERE KeywordID NOT IN положения, в зависимости от того, есть ли совпадение.

Принять к сведению, чтобы рассмотреть вопрос о том, является ли случай каждого ключевого слова важным для вашего бизнес-кейса и, соответственно, учитывать сортировку (чувствительную к регистру или нечувствительность).

+0

Есть ли какой-либо вариант, который я могу отделить от текста в C# в соответствии с условием 'AND' или' Not', а затем искать его в SQL? – Gitz

+0

Я предлагал использовать C# в качестве альтернативы использованию TSQL/DML для создания одноразовой совокупности новых таблиц. Это можно сделать без использования C#. Что касается поиска, разделение терминов может быть сделано в TSQL или из него (это не сложно), но в основном вы ищете совпадения в таблице столярных: - одно соединение или 'WHERE IN' для каждого include/match термин и один для каждого исключающего термина. – CJBS

3

Думаю, вы можете легко решить эту проблему, создав FULL TEXT INDEX на своей KWD колонке. Затем вы можете использовать запрос CONTAINS для поиска фраз. Индекс FULL TEXT выполняет функцию пунктуации и автоматически игнорирует запятую.

-- If search text is = Man,Businessman then the query will be 
SELECT AS_ID FROM tbl_main 
WHERE CONTAINS(KWD, '"Man" AND "Businessman"') 

-- If search text is = Man,-Businessman then the query will be 
SELECT AS_ID FROM tbl_main 
WHERE CONTAINS(KWD, '"Man" AND NOT "Businessman"') 

-- If search text is = woman,girl,-Working the query will be 
SELECT AS_ID FROM tbl_main 
WHERE CONTAINS(KWD, '"woman" AND "girl" AND NOT "working"') 

Для поиска нескольких слов (например, в mobile phone в вашем случае) использовать процитированные фразы:

SELECT AS_ID FROM tbl_main 
WHERE CONTAINS(KWD, '"woman" AND "mobile phone"') 

Как прокомментировал ниже цитируемых фраз важны во всех поисков, чтобы избежать плохих поисков в случае например когда термин поиска «таблетка работает» и значение KWD составляет woman,girl,Digital Tablet,working,sitting,online

Имеется специальный чехол для одного поискового запроса -. NOT не может использоваться в качестве первого члена в CONTAINS. Таким образом, следует использовать запрос, как это:

-- If search text is = -Working the query will be 
SELECT AS_ID FROM tbl_main 
WHERE NOT CONTAINS(KWD, '"working"') 
+0

В случае записи № 6 «6 женщина, девушка, цифровая таблетка, работа, сидение, онлайн», если входной поиск был «планшетным», это приведет к совпадению (игнорируя случай ввода текста) ? – CJBS

+0

Да, точно! соглашайтесь с @CJBS – Gitz

+0

Используйте приведенные фразы. Я обновлю свой ответ – cha

0

Я предпочел бы решение Cha, но вот еще одно решение:

declare @QueryParts table (q varchar(1000)) 
insert into @QueryParts values 
('woman'), 
('girl'), 
('-Working') 

select AS_ID 
from tbl_main 
inner join @QueryParts on 
(q not like '-%' and ',' + KWD + ',' like '%,' + q + ',%') or 
(q like '-%' and ',' + KWD + ',' not like '%,' + substring(q, 2, 1000) + ',%') 
group by AS_ID 
having COUNT(*) = (select COUNT(*) from @QueryParts) 
1

При такой конструкции, вы бы две таблицы. Один, который определяет идентификаторы и подтаблицу, которая содержит набор ключевых слов для каждой строки поиска.

Аналогичным образом вы преобразуете строки поиска в две таблицы, одну для строк, которые должны совпадать, и одну для строк с отрицанием. Предполагая, что вы поместили это в хранимую процедуру, эти таблицы будут параметрами таблицы.

После того как вы это установить, запрос просто написать:

SELECT M.AS_ID 
FROM tbl_main M 
WHERE (SELECT COUNT(*) 
     FROM tbl_keywords K 
     WHERE K.AS_ID = M.AS_ID 
      AND K.KWD IN (SELECT word FROM @searchwords)) = 
     (SELECT COUNT(*) FROM @searchwords) 
    AND NOT EXISTS (SELECT * 
        FROM tbl_keywords K 
        WHERE K.AS_ID = M.AS_ID 
        AND K.KWD IN (SELECT word FROM @minuswords)) 
Смежные вопросы