2017-03-09 89 views
0

我需要编写一个select查询,该查询将搜索单个表中从用户输入查询字符串(如"John Doe Engineering")中获取的单词。该字符串可以由单个或多个单词组成。查询字符串将作为参数传递给存储过程。总共有大约20列需要被搜索。我首先想到的是这样的:选择查询以匹配具有多个关键字的多个列

SELECT * 
FROM Employees 
WHERE FirstName LIKE '%John%' OR FirstName LIKE '%Doe%' OR FirstName LIKE '%Engineering%' 
WHERE LastName LIKE '%John%' OR LastName LIKE '%Doe2%' OR LastName LIKE '%Engineering%' 
WHERE Manager LIKE '%John%' OR Manager LIKE '%Doe%'OR Manager LIKE '%Engineering%' 
WHERE Department LIKE '%John%' OR Department LIKE '%Doe%'OR Department LIKE '%Engineering%' 
--repeat for 16 more table columns 

但我不知道如何以最佳方式生成基于用户查询字符串输入的查询语法。此外,这似乎是一个非常低效的查询。在这种情况下使用full text search会更好吗?我想知道最好的方法是什么?

+1

SQL Server有一个叫做全文搜索功能。可能你想在这里。 – Hogan

+1

我在想“全文搜索”。 –

+1

@GordonLinoff - 有3秒钟! :D – Hogan

回答

-1

正如其他人所说 - 全文搜索可能是这类事情的最佳解决方案。也就是说,我认为提供T-SQL解决方案会很有趣。

快速免责声明1

*我强烈建议您不要使用以下解决方案 - 这是旨在成为一个有趣的小SQL锻炼;表现会很糟糕。而且 - 我演示了两个非常有效的方式来分割字符串:一个使用PARSENAME *

快速免责声明2

使用杰夫MODEN的DelimitedSplit8K,其他的技术我应该链接该列插入指出一个问题作为一对夫妇建议的单个字符串 - 它可能导致误报;考虑下面的查询:

DECLARE @search varchar(100) = 'ab'; 

WITH sampleData AS (SELECT fn, ln FROM (VALUES ('aa', 'bb'), ('cc', 'dd')) t(fn,ln)) 
SELECT * 
FROM sampleData 
WHERE CONCAT(fn,ln) LIKE '%'[email protected]+'%'; 

上面的查询将返回即使“AB”并不在任一列中存在的第一个记录。出于这个原因,你会改变(在约翰的例子或CHARINDEX)在哪里找这样的:

WHERE CONCAT(fn, '|||', ln) LIKE '%'[email protected]+'%'; 

我的解决方案

-- SAMPLE DATA 
------------------------------------------------- 
DECLARE @employees TABLE 
(
    FirstName varchar(100), 
    LastName varchar(100), 
    Manager varchar(100), 
    Department varchar(100) 
); 

INSERT @employees 
SELECT * 
FROM 
(
    VALUES 
    ('bob', '****', 'ddd', 'sss'), 
    ('fff', 'fred', 'obx', 'ccc'), 
    ('Sue', 'abcd', 'ddd', 'zzz'), 
    ('ddd', 'dcba', '123', 'fobbb') 
) xx(x1, x2, x3, x4); 

-- Solution #1: when @search has <= 4 "items" 
------------------------------------------------- 
DECLARE @search varchar(100) = 'xx bb ff zz'; 

SELECT e.* 
    --,PARSENAME(REPLACE(@search,' ','.'), N) AS matchedPattern 
FROM (VALUES (1),(2),(3),(4)) t(n) 
CROSS JOIN @employees e 
WHERE 
    CHARINDEX 
    (
    PARSENAME(REPLACE(@search,' ','.'), N), 
    CONCAT(FirstName, '|||', LastName, '|||', Manager, '|||', Department) 
) > 0; 

-- Solution #2: when @search has (or can have) > 4 "items" 
------------------------------------------------- 

-- for this you will need delimitedsplit8k: http://www.sqlservercentral.com/articles/Tally+Table/72993/ 
SELECT e.* 
FROM dbo.delimitedsplit8k(@search, ' ') 
CROSS JOIN @employees e 
WHERE 
    CHARINDEX 
    (
    item, 
    CONCAT(FirstName, '|||', LastName, '|||', Manager, '|||', Department) 
) > 0; 
+0

组合列的想法是可怕的。这会导致性能下降。正如我在我的评论中提到的 –

+0

@ t-clausen.dk - 这就是为什么我说“全文搜索可能是最好的解决方案”,然后补充说我包括一个T-SQL解决方案“为了好玩”。你提供的只是一个评论,包括OP在内的每个人都已经知道。我会对你发布的解决方案感兴趣.... –

+0

你实际上是投我的[回复](http://stackoverflow.com/questions/42509024/get-string-between-2-characters-that-重复 - 几次在SQL服务器/ 42509706#comment72537334_42509706)复仇的另一个问题,我投下了这个投票? –