(MS SQL)如何实现相关文章功能(多关键字匹配)

前言:大家或许会觉得很惊讶:为什么灵感之源会讨论SQL?或许应该这样说吧:搞业务系统,不跟SQL扯上关系似乎比较难。

场景:在开发某系统的过程中,我遇到了要实现“相关文章”功能:任何文章都可以定义“关键字”,每篇文章依靠这个“关键字”来确定其它文章是否跟它相关,如果没有定义关键字,则可能需要使用全文检索来实现了,这是别的话题了。

思考:因为允许关键字可以通过“,”分隔符号来定义多个,所以加大了难度。经过思考,可以通过在保存文章的时候便分解关键字,建立一关键字表,把所有关键字逐个按对应的文章ID来保存。并决定采用纯SQL存储过程的办法,因为这种重复的操作,与其用通用函数,倒不如用预编译的存储过程更加快,这样能把所有处理都留給SQL Server。

解决方案:首选要做的是在原有文章表(Details)的基础上建立相关文章表(RelatedDetails),字段包括ItemID(主键)、DetailID(文章ID)和Keyword(关键字)。以下是主要存储过程:

1、UpdateRelatedDetails:更新相关文章关键字

CREATE procedure dbo.UpdateRelatedDetails

@DetailID 
INT,
@Keywords 
NVARCHAR(500)

AS

EXEC DeleteRelatedDetails @DetailID

DECLARE @I INT
DECLARE @Keyword NVARCHAR(50)

SET @Keywords=REPLACE(@Keywords,''',')
SET @Keywords=REPLACE(@Keywords,''',')
SET @Keywords=RTRIM(LTRIM(@Keywords))

SET @I=CHARINDEX(',', @Keywords)

WHILE @I>=1
    
BEGIN
        
SET @Keyword=LEFT(@Keywords, @I-1)
        
INSERT INTO DetailKeywords (DetailID, Keyword) VALUES(@DetailID, @Keyword)
        
SET @Keywords=SUBSTRING(@Keywords, @I+1,LEN(@Keywords)-@I)
        
SET @I=CHARINDEX(',', @Keywords)
    
END

IF @Keywords<>''
    
INSERT INTO DetailKeywords (DetailID, Keyword) VALUES(@DetailID, @Keywords)
GO

2、DeleteRelatedDetails:删除原有相关文章关键字
CREATE PROCEDURE dbo.DeleteRelatedDetails

@DetailID 
INT

AS

DELETE FROM DetailKeywords WHERE DetailID=@DetailID
GO

3、GetRelatedDetails:获取相关文章,其中Details就是文章表
CREATE procedure dbo.GetRelatedDetails

@DetailID 
INT

AS

DECLARE @Keywords NVARCHAR(500)

SELECT @Keywords=Keywords FROM Details WHERE ItemID=@DetailID

IF @Keywords<>''
    
BEGIN
        
SELECT DISTINCT d.ItemID, d.Subject, d.ItemFile
        
FROM Details d RIGHT OUTER JOIN DetailKeywords k ON k.DetailID=d.ItemID         WHERE d.ItemID <> @DetailID AND @Keywords LIKE '%'+k.Keyword+'%'
    
END
GO


代码很简单,但希望能給大家带来一点思考:)

p.s.因为前台是使用.NET写的,那这个帖子也算是部分.NET技术了,呵呵。
posted @ 2004-10-21 19:28 灵感之源 阅读(3187) 评论(13)  编辑 收藏 网摘 所属分类: 2.技术交流

  回复  引用    
#1楼2004-10-21 19:32 | iCeSnaker
可以留作参考之用,顺便占个座
  回复  引用  查看    
#2楼2004-10-21 19:39 | boy119      
提问:
灵感为什么不在文章表(Details)中加入关键字字段而是新建一个表呢?

  回复  引用  查看    
#3楼2004-10-21 19:43 | boy119      
不好意思,刚才没有看明白,现在明白了。之所以加入一个新表是为了把多个关键字分开,这样的数据库设计更加合理。
  回复  引用  查看    
#4楼2004-10-21 19:52 | Rover      
数据库是合理了,但一个表膨胀的太厉害了,当我要查询一个同一个关键字的所有文章,要扫描这个表,这个表的记录太大了,效率高吗?
  回复  引用  查看    
#5楼2004-10-21 20:06 | boy119      
但是,除了这个方法外,你还有效率更高的方法吗?
  回复  引用  查看    
#6楼2004-10-21 21:05 | 吕震宇      
一篇文章可能会有多个关键字,按照文章的比较仅仅是片面的比较。如果实现更复杂的功能的化,可以考虑使用关联分析,通过统计的方法进行关联度分析。所谓的全文检索,实际操作过程便是:分词、词频统计、根据词频统计信息建立反向检索库。当文章聚类时,实际上便是词频向量的聚类(这个过程可能需要降维)。使用神经网络也可以达到这个目的,CC4神经网络就是一个不错的算法。

呵呵,以前做过一个项目,叫《模糊信息优化技术在异构数据库中的应用》,谈谈体会。

  回复  引用  查看    
#7楼[楼主]2004-10-21 21:09 | unruledboy(灵感之源)      
呵呵,晕吖,震宇兄,确实惭愧,我仅仅是实现最基本的功能,跟智能技术确实不能比较,呵呵。
  回复  引用  查看    
#8楼2004-10-26 23:08 | msolap      
关键字使用单独的表来存储是必要的,特别是吕兄的方案。
只是最后一个存储过程GetRelatedDetails的做法值得商榷,Rover的担心好像还是有点道理的。
1. Details.Keywords字段和DetailKeywords表内的信息冗余,如果这个冗余能带来查询性能上的提升,那还可以考虑,但这个好像看不出...
2. LIKE关键字导致表扫描,且DetailKeywords表Keyword字段上的索引派不上用场,可以预见这个执行效率会很低。事实上,关键字匹配的所有信息已经在DetailKeywords表里有了,就没有必要和Details表Join了,所以相信有更好的算法...
3. 对写MS SQL存储过程再提点建议:存储过程开头最好加上
SET NOCOUNT ON

  回复  引用    
#9楼2005-10-27 09:03 | jeckey[未注册用户]
我想问一下 如何在c++中实现括号的匹配问题.括号包含有:{},[];(). 谢谢大家
  回复  引用    
#10楼2005-12-04 20:44 | simeonz[未注册用户]
最近正弄这个,gogole一下看到这贴...
这问题好象不用存储过程,业务用select串直接解决好象很容易的可以用循环生成条件串where keyword like ... or keyword like ...
但用存储过程还真有些难弄啊..呵呵..
建个单独的表来存储好象没必要吧,直接用临时表算了..也就解决了Rover所担心问题..

改进了一下上面的存储过程如下..参数@TopNum为显示的最大相关文章条数

CREATE PROCEDURE [dbo].[SP_DOC_GetRelatedDocument]
(
@ID int, -- DocumentID
@TopNum int
)
AS

DECLARE @Keys nvarchar(100)
DECLARE @I int
DECLARE @Keyword nvarchar(50)

CREATE TABLE #Temp (Keyword nvarchar(100))
SET @Keys= (select Keyword from DB_DOC_Document where ID=@ID)
SET @I=CHARINDEX('|', @Keys)

WHILE @I>=1
BEGIN
SET @Keyword=LEFT(@Keys, @I-1)
INSERT INTO #Temp ( Keyword) VALUES(@Keyword)
SET @Keys=SUBSTRING(@Keys, @I+1,LEN(@Keys)-@I)
SET @I=CHARINDEX('|', @Keys)
END

IF @Keys<>''
INSERT INTO #Temp (Keyword) VALUES(@Keys)

set rowcount @TopNum
SELECT DISTINCT
ID,
Title,
Author,
Hits,
AddedDateTime
FROM
DB_DOC_Document as d, #Temp as t
WHERE
d.Keyword LIKE '%'+t.Keyword+'%'
and
ID <>@ID
and
Audit =1
ORDER BY
AddedDateTime DESC
set rowcount 0
GO

  回复  引用    
#11楼2006-04-10 16:20 | 网上购物[未注册用户]
又学了一点小东东
  回复  引用    
#12楼2007-02-23 10:30 | net[未注册用户]
不错 很受启发
  回复  引用    
#13楼2008-04-12 22:48 | simeonz[未注册用户]
呵呵,一晃两年多了,没想到内容还在,这个问题还可以简化的,过两天有时间再来写个..



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 55190




相关文章:

相关链接: