跨多个表的SQL简单搜索功能

时间:2014-03-26 13:48:43

标签: sql sql-server search

我正在使用SQL Server 2012.我需要使用单个文本字段来实现搜索功能。

假设我有下表:

--------------------------------------------------------------------------------
FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR    
--------------------------------------------------------------------------------
John        Doe         Boston      2005        Mc Donald   
Marc        Forestier   Bruxelle    2010        Private bank    
Céline      Durand      Paris       1999        Food SA     
Simon       Forestier   Toulouse    2001        Forestier SARL  
John        Smith       New York    1992        Events Org. 
Sonia       Grappe      Toulon      2010        Forestier SARL  
--------------------------------------------------------------------------------

行为如下:

  • 必须在所有列上搜索所有单词(空格分隔)。
  • 应为每个单词应用LIKE
  • 如果只搜索了一个单词,则返回包含该单词的所有记录
  • 如果搜索了多个单词,则仅返回包含不同字数最多的记录(请参阅下面的“forestier”示例)
  • 我需要一个查询,没有TSQL

我尝试过很多东西,但它并不像看起来那么简单。

一些例子:

“约翰”:

-------------------------------------------------------------------------------
FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR
-------------------------------------------------------------------------------
John        Doe         Boston      2005        Mc Donald
John        Smith       New York    1992        Events Org.
-------------------------------------------------------------------------------

“John Doe”:

-------------------------------------------------------------------------------
FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR
-------------------------------------------------------------------------------
John        Doe         Boston      2005        Mc Donald
-------------------------------------------------------------------------------

“弗赖斯”:

-------------------------------------------------------------------------------
FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR
-------------------------------------------------------------------------------
Marc        Forestier   Bruxelle    2010        Private bank
Simon       Forestier   Toulouse    2001        Forestier SARL
Sonia       Grappe      Toulon      2010        Forestier SARL
-------------------------------------------------------------------------------

“for 2010 xelle”:

FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR
--------------------------------------------------------------------------------
Marc        Forestier   Bruxelle    2010        Private bank
--------------------------------------------------------------------------------

此示例使用单个表;实际上我的5列来自4个不同的表,因此实现全文搜索会有点复杂!

3 个答案:

答案 0 :(得分:2)

如何添加其他字段,例如包含来自其他字段的所有信息的文本字段。

FIRSTNAME   LASTNAME    CITY        PROMOYEAR   EMPLOYOR   SEARCHFIELD
John        Doe         Boston      2005        Mc Donald  John Doe Boston 2005 Mc Donald

并在此字段上进行搜索。它不优雅,但它可以工作。

在下面添加:

我不认为SQL语法支持您的所有需求,但您可以进行另一种解决方法。创建一个包含您要搜索的所有单词的表格:

create table searchtable
(
rowid int, --key to the id for the row in your table
mothertableName varchar(), -- name of the table if necessary
motherfieldName varchar(), -- name of field
word varchar() -- the actual word to be searchable
)

搜索单词及其出现次数最多的地方:

SELECT * FROM myTable WHERE id IN(
   SELECT rid as id, MAX(c) FROM (
      SELECT rowid as rid, COUNT(rowid) as c FROM Searchtable WHERE word IN ('john','doe')
   )
)

上面的SQL肯定不会起作用,但我希望你能理解我提出的建议。您应该获得一行中搜索单词数量最多的行。但是' IN' SQL中的运算符要求您创建一些动态SQL。

当你写这篇文章时,你已经尝试了几乎所有的东西,我认为SQL不能单独完成。

答案 1 :(得分:2)

这似乎是Sql Full Text Indexing设计的工作。

AFAIK全文索引不适用于数字类型,因此您可能需要为任何日期或数字类型添加计算列,例如如果PromoYear是数字:

ALTER TABLE MyTable
   ADD TextPromoYear AS CAST(PromoYear AS NVARCHAR(4))
   PERSISTED;

您需要在数据库中设置全文目录:

CREATE FULLTEXT CATALOG CAT_MyCat AS DEFAULT;

假设您有一个名为PK_MyTable的主键,请创建全文索引:

CREATE FULLTEXT INDEX ON MyTable(FirstName, LastName, City, TextPromoYear, 
                                 Employer) 
KEY INDEX PK_MyTable;
<德尔> 幸运的话,您可以使用[`CONTAINS`](http://technet.microsoft.com/en-us/library/ms187787.aspx)或`FREETEXT`执行查询,例如:     选择 *     来自mytable     在哪里包含((FirstName,LastName,City,TextPromoYear,Employer),                    '为OR 2010 OR xelle') 查询语法并不完全符合您的要求,但您可以根据`CONTAINS`的要求调整所需的“空格分隔”查询。

修改

这并不像这样容易。通配符*只能用作后缀,您还需要聚合列以搜索所有列。

完整条款:

SELECT *
FROM mytable
WHERE CONTAINS(*, 'John and Doe');

对于部分搜索,您可能需要使用vanilla LIKE来测试未知前缀(*xelle)

SELECT *
FROM mytable
WHERE CONTAINS(*, '"for*"  and 2010') AND SearchableComputedColumn like '%elle%';

Updated SqlFiddle here

答案 2 :(得分:0)

这是一个解决方案。

我将搜索限制为6个字。 对于每个单词,我检查它是否存在于连接列中。 我得到了一个&#34;得分&#34;对于每条记录,每次在其中找到一个单词时添加+1。 我返回得分最高的记录。

功能:

CREATE FUNCTION [dbo].[SEARCH_SINGLE] (
    @langId INT = 4,
    @searchString VARCHAR(MAX) = NULL
)
RETURNS TABLE
AS
RETURN
WITH words AS (
    SELECT Name as Val, ROW_NUMBER() OVER(ORDER BY Name) as Num FROM [dbo].splitstring(@searchString, ' ')
),
results AS (
    SELECT DISTINCT
        ...
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 1 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END +
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 2 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END +
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 3 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END +
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 4 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END +
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 5 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END +
        CASE WHEN EXISTS(SELECT 1 FROM words WHERE Num = 6 AND (ISNULL(a.[FIRSTNAME], '') + ' ' + ISNULL(a.[LASTNAME], '') + ' ' + ISNULL(c.[CITY], '') + ' ' + ISNULL(j.[PROMO_YEAR], '') + ' ' + ISNULL(e.[EMPLOYOR], '')) like '%'+Val+'%') THEN 1 ELSE 0 END as Nb
    FROM
        ...
    WHERE
        ...
)
SELECT 
    ...
FROM
    results
WHERE
    Nb = (SELECT MAX(Nb) FROM results)
    AND Nb <> 0

评论