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

Posted

技术标签:

【中文标题】跨多个表的 SQL 简单搜索功能【英文标题】:SQL simple search functionality across multiple tables 【发布时间】:2014-03-26 13:48:43 【问题描述】:

我使用的是 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.
-------------------------------------------------------------------------------

“约翰·多伊”:

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

“2010 xelle”:

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

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

【问题讨论】:

“我需要一个查询,没有 TSQL”是什么意思?为什么不展示您尝试过的许多事情中的一两个。 听起来像是全文搜索的工作。 technet.microsoft.com/en-us/library/ms142571.aspx @KarlKieninger 抱歉,我的意思是没有动态查询,因为我需要使用表值函数来运行查询。 @KlasLindbäck 你是对的,但我以一个表为例,实际上我的 5 列来自 4 个不同的表,所以实现全文搜索有点复杂! @Yann39 跨多个表搜索字段是一个非常不同的问题。我认为你的例子过于简单化了。相反,您应该提供一张至少有两张桌子的桌子。我怀疑您可以通过索引视图的全文搜索来完成此操作。 ***.com/questions/8486703/… 【参考方案1】:

这似乎是 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` 执行查询,例如: 选择 * 来自我的表 WHERE CONTAINS((FirstName, LastName, City, TextPromoYear, Employer), '对于 OR 2010 OR xelle') 查询语法并不完全符合您的要求,但您可以根据 `CONTAINS` 的要求将所需的“空格分隔”查询调整为 ` 和 `。

编辑

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

完整条款:

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

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

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

Updated SqlFiddle here

【讨论】:

我认为这是解决最初问题的正确方法;)但我正在寻找更快/更简单的解决方案。我过度简化了我的示例,但实际上我的 5 列来自 4 个不同的表,我认为它对于我们的需求来说太复杂了。【参考方案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 肯定行不通,但我希望你能明白我的建议。你应该得到一个搜索词数量最多的行。但是 SQL 中的“IN”运算符要求您动态创建一些 SQL。

正如您所写,您几乎尝试了所有方法,我认为 SQL 无法单独完成。

【讨论】:

我在第一个答案中添加了一些内容。见上文。 你给了我一些想法!所以我终于设法找到了一个解决方案,我将它作为答案发布,因为我认为这是一个更好的解决方案,它涵盖了所有的初始要求。【参考方案3】:

这里有一个解决方案。

我已将搜索限制为 6 个单词。 对于每个单词,我检查它是否存在于连接列中。 每次在其中找到一个单词时,我都会通过添加 +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

评论?

【讨论】:

以上是关于跨多个表的 SQL 简单搜索功能的主要内容,如果未能解决你的问题,请参考以下文章

在多个表的所有列中搜索的最简单方法? [复制]

Rails 跨多个模型搜索

跨多个列 PostgreSQL/Rails 的慢速通配符搜索 LIKE

SQL在同一字段中搜索多个值

JIRA的使用介绍(四)- 高级搜索(JQL)

用于搜索具有无限个位域的表的 SQL 设计方法