SQL使用特殊字符自定义搜索

时间:2015-06-22 05:14:28

标签: sql sql-server sql-server-2008

我正在创建一个关键字措辞模块,我想用逗号分隔的单词搜索数据。搜索分为逗号,和减号-。 我知道关系数据库引擎的设计原则是单元格保存单个值并遵守此规则可以有助于提高性能。但在这种情况下,表格已经运行并且拥有数百万个数据并且无法更改表格结构

看看示例我想要做的是

我在SQL

中有一个主表名tbl_main
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 
  

如果搜索文本为= Man,则商家结果AS_ID为= 1,2

     

如果搜索文本为= Man,-Businessman,则结果AS_ID为= 3

     

如果搜索文本是=女人,女孩, - 工作,那么结果AS_ID是= 4,5

     

如果搜索文本是=女性,女孩则结果AS_ID = 4,5,6,7

为什么这样做最好,帮助非常感谢。谢谢提前

5 个答案:

答案 0 :(得分:3)

我认为您可以通过在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"')

答案 1 :(得分:2)

以下是我尝试使用Jeff 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

这个使用Dwain Camps在此article讨论的关系部门没有剩余问题的解决方案。

答案 2 :(得分:1)

根据您所描述的内容,您希望搜索文本中包含的关键字与KWD列中的匹配项以及带有前缀的那些关键字匹配-被排除在外。

尽管以这种格式存在数据,但仍然最有意义的是规范化数据,然后根据关键字的存在与否进行查询。

要用非常粗略的术语来做到这一点: -

  1. 创建另外两个表 - Keywordtbl_Main_KeywordKeyword包含每个可能关键字的不同列表,tbl_Main_Keyword包含tbl_Main到每个Keyword记录中每个记录之间的链接。确保在关键字的文本字段上创建索引(例如Keyword.KeywordText列,或您调用的任何内容),以及KeywordID表中的tbl_Main_Keyword字段。在表之间创建外键。
  2. 编写一些DML(或使用单独的程序,例如C#程序)来遍历每条记录,解析文本,并将遇到的每个不同关键字插入Keyword表。为tbl_main记录中的每个关键字创建一个关系。
  3. 现在,要进行搜索,请将搜索文本解析为关键字,并针对包含tbl_Main_KeywordWHERE KeywordID IN子句的WHERE KeywordID NOT IN表撰写查询,取决于是否有匹配。
  4. 请注意考虑每个关键字的大小写是否对您的业务案例很重要,并相应地考虑排序规则(区分大小写或不区分大小写)。

答案 3 :(得分: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)

答案 4 :(得分:0)

通过这样的设计,您将拥有两个表。一个定义ID和一个子表,每个搜索字符串包含一组关键字。

同样,您可以将搜索字符串转换为两个表,一个用于应匹配的字符串,另一个用于否定字符串。假设您将它放在存储过程中,这些表将是表值参数。

完成此设置后,查询很简单:

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))