SQL算法麻烦 - 用于添加和过滤结果的循环

时间:2009-07-08 22:22:42

标签: .net sql search

我正在使用SQL搜索功能再次努力。

基本上我想要一个存储过程,它将一个字符串拆分成单词,然后对于我想要使用全文搜索检查的每个单词,如果它在2个表中的任何一个中。

如果在这些表中找到了结果,它应该将它们添加到它为其他单词找到的任何结果中,并返回一个集合,其中记录在这两个集合中。

所以这是我的算法

if null return all restaurants

declare results = empty

for each word
if in restaurant.name or cuisine.name
    addRowsToResults 
else if in restaurant.city
    addRowsToResults 
else if in restaurant.postcode
    addRowsToResults 


addRowsToResults 
results = (results + new results) where a match is in both

我不知道从哪里开始,我已经搜索谷歌多年,但作为SQL的初学者我可能会遗漏一些条款。

这也是做这种事情的最佳方法吗?

感谢任何帮助。

修改 只是为了提供更多信息。 City,Postcode和Name都是Restaurants表中的nvarchar字段。菜肴名称位于不同的餐桌中,并通过另一张桌子与餐厅相连,因为餐厅可以提供多种菜肴。

所有这些字段都有全文搜索索引。

7 个答案:

答案 0 :(得分:1)

这不是SQL的工作方式。你可以获得一些相同的功能,但它看起来不像你要问的那样;你正在以更加程序化的方式写作(自然而然)。

一方面,“添加结果”试图将(可能)不同的东西整合到一个集合中。你是 - 我认为 - 要​​求餐馆和美食,城市和邮政编码都进入同一个集合,而SQL(粗略地说)的唯一一种集合是表格,在表格中,所有行都有相同的类型。

但也许餐馆和美食,城市和邮政编码都是同一张桌子的所有字段?那么,对于给定的单词,你可以说

SELECT * 
FROM   your_table
WHERE  restaurant like "%" + word + "%"
OR     cuisine    like "%" + word + "%"
OR     city       like "%" + word + "%"
OR     postcode   like "%" + word + "%";

当你想要匹配多个单词时,它开始变得复杂;这个答案是作为一个起点;也许一旦你对SQL更熟悉,你可以提出更容易回答的问题。祝你好运!

根据您的表格描述

更新

SELECT r.*
FROM       restaurant r
INNER JOIN link       k ON k.restaurant_id = r.restaurant_id
INNER JOIN cuisine    c on c.cuisine_id    = k.cuisine_id
WHERE  r.restaurant like "%" + word + "%"
OR     c.name       like "%" + word + "%"
OR     r.city       like "%" + word + "%"
OR     r.postcode   like "%" + word + "%";

不要担心哪些记录“先”回来。您的数据库速度足够快,不应该成为一个问题。

答案 1 :(得分:1)

分词function here。返回可以加入的表格。

您还应该调查metaphone或soundex,但为了支持您需要在单独的表格中预先索引您的数据,或者在单独的字段中预先计算每个单词的美式互联网或soundex代码,并对其进行索引。

答案 2 :(得分:1)

看起来您试图采用基于集合的声明性查询语言的过程方法。

首先,您可以使用表值用户定义函数将字符串拆分为结果集。像下面这样的东西会做 -

CREATE function [dbo].[csl_to_table] ( @list nvarchar(MAX) )
RETURNS @list_table TABLE ([id] nvarchar(20)) -- set this to your maximum size string
AS
BEGIN
    DECLARE     @index INT,
                @start_index INT,
                @id nvarchar(20)  -- set this to your maximum size string

    SELECT @index = 1 
    SELECT @start_index = 1
    WHILE @index <= DATALENGTH(@list)
    BEGIN

        IF SUBSTRING(@list,@index,1) = ','
        BEGIN

                SELECT @id = SUBSTRING(@list, @start_index, @index - @start_index) 
                INSERT @list_table ([id]) VALUES (@id)
                SELECT @start_index = @index + 1
        END
        SELECT @index  = @index + 1
    END
    SELECT @id = SUBSTRING(@list, @start_index, @index - @start_index )
    INSERT @list_table ([id]) VALUES (@id)
    RETURN
END

然后使用结果集,您可以加入要查询匹配的表,并返回匹配的结果集。

答案 3 :(得分:1)

这是一个split函数,它有一个分隔符的可选参数......

CREATE FUNCTION [dbo].[fnSplit]( @List VARCHAR(4000), @Delimiter CHAR(1) = ',' )
    RETURNS @Result TABLE (item VARCHAR(100))
    BEGIN
        DECLARE @Item VARCHAR(100)
        WHILE CHARINDEX(@Delimiter,@List,0) <> 0
        BEGIN
            SELECT @Item = SUBSTRING(@List,1,CHARINDEX(@Delimiter,@List,0)-1)
                , @List = SUBSTRING(@List,CHARINDEX(@Delimiter,@List,0)+1,LEN(@List))

            IF LEN(@Item) > 0
                INSERT INTO @Result
                    SELECT @Item
        END
        IF LEN(@List) > 0
            INSERT INTO @Result
                SELECT @List
        RETURN
    END
GO

然后你的查询看起来像这样...

SELECT DISTINCT
      'restaurants' AS [source]
    , r.ID AS [ID] --Assuming ID is your primary key column
    , r.name + '(' + r.city + ', ' + r.state + ')' AS [Description]
FROM restaurants AS [r]
JOIN [dbo].[fnSplit](@Query,' ') AS [terms]
ON
    ISNULL(r.name,'')
    +','+ISNULL(r.city,'')
    +','+ISNULL(r.postcode,'') LIKE '%'+terms.item+'%'

UNION SELECT DISTINCT
      'cuisine' AS [source]
    , r.ID AS [ID] --Assuming ID is your primary key column
    , r.name AS [Description]
FROM cuisine AS [r]
JOIN [dbo].[fnSplit](@Query,' ') AS [terms]
ON
    ISNULL(r.name,'') LIKE '%'+terms.item+'%'

fnSplit 函数将查询字词分解为表变量中的行并返回它。然后,select查询将与结果表连接。查询是不同的,因此无论查询中匹配的项数是多少,都只返回一行的实例。连接条件很容易被分解为一系列AND / OR条件,但我认为LIKE操作比连接更昂贵所以我只是连接列。

<强>更新

由于启用了全文索引,因此可以使用以下简化查询。

DECLARE @Phrase VARCHAR(4000)
SELECT @Phrase = item + '" OR "' FROM [dbo].[fnSplit](@Query,' ')
SET @Phrase = SUBSTRING(@Phrase,0,LEN(@Phrase)-6)


SELECT DISTINCT
      'restaurants' AS [source]
    , r.ID AS [ID] --Assuming ID is your primary key column
    , r.name + '(' + r.city + ', ' + r.state + ')' AS [Description]
FROM restaurants AS [r]
WHERE
    CONTAINS(r.name,@Phrase)
    OR CONTAINS(r.city,@Phrase)
    OR CONTAINS(r.postcode,@Phrase)

UNION SELECT DISTINCT
      'cuisine' AS [source]
    , r.ID AS [ID] --Assuming ID is your primary key column
    , r.name AS [Description]
FROM cuisine AS [r]
WHERE CONTAINS(r.name,@Phrase)

答案 4 :(得分:0)

即使我想,我也无法就此发表完整的故事 - 您尚未公布表格定义或任何样本数据。

那就是说,我会做一些猜测。首先,我认为您需要以基于集合的方式使用SQL。不要考虑逐行处理 - 处理集合。然后将这些组合在一起。

答案 5 :(得分:0)

如果您在SQL中寻找For Each循环,则大多数SQL方言都有游标,允许您使用某些查询选择所有记录,然后使用Cursor迭代记录。

答案 6 :(得分:0)

要使用全文搜索,您需要使用CONTAINS关键字。查看如何在网上书籍中使用它(我必须参加会议或者我会提供一个例子)。