如何解决SQL Server-选择关键字和同义词列表
| 我有两个表: 关键词 我在其中存储唯一关键字。CREATE TABLE [dbo].[Keywords]
[KeywordID] [int] IDENTITY(1,1) NOT NULL,[Description] [varchar](200) NOT NULL
select * from Keywords
1 MVC
2 HTML
3 C#
4 ASP.NET MVC
5 MVC3
关键字同义
我指出某些关键字是其他关键字的同义词。
CREATE TABLE [dbo].[KeywordSynonymous]
[KeywordID] [int] NOT NULL,[KeywordSynonymousID] [int] NOT NULL
这两个字段在关键字表中均为FK,并且两个组合字段均在此表中用作PK。
在这里,我要声明\'MVC \'和\'MVC3 \'是同义词,也许\'MVC3 \'和\'ASP.NET MVC \'也是同义词。
select * from KeywordSynonymous
1 5
5 4
概念
1)
如果关键字\'MVC \'是\'MVC3 \'的同义词
和\'MVC3 \'是\'ASP.NET MVC \'的同义词
那么从概念上讲MVC也是\'ASP.NET MVC \'的同义词
2)
如果关键字\'MVC \'是\'MVC3 \'的同义词
那么VICEVERSA也是正确的,并且'MVC3是''MVC \'的同义词
题
想象一下,在我的网站上,我正在搜索并且用户可以键入任何内容,但是对于我们的示例,他可以键入\'MVC \'或\'MVC3 \'...
如何使用一条SQL语句获得所有可能的同义词,以确保同时满足Concept 1和Concept 2?
意思是:
>> if the user types \'MVC\',my sql should return \'MVC,MVC3\',\'ASP.NET MVC\'.
>> if the user types \'MVC3\',\'ASP.NET MVC\'.
>> if the user types \'ASP.NETMVC\',\'ASP.NET MVC\'.
================================================== ==============
更新
我觉得我必须添加一些关于我正在开发的网站的信息。在这个市场上,年轻的专业人员将能够使用新的方式来推销自己的服务。
由于我们想允许任何职业,所以我目前无法预见什么“关键词”将更好地定义每个职业。因此,我将允许用户定义这些关键字。
我的问题是我需要允许UserX按专业和关键字搜索这些年轻的专业人员。我需要允许这些用户将其搜索到的关键字与现有关键字进行匹配,以便当前和将来的搜索将自动匹配正确的配置文件。
这就是为什么我事先没有所有关键字,并且肯定无法识别将来的关键字及其各自的同义词的原因。我也不能期望用户将所有现有关键字匹配到所有相关关键字...所以这就是为什么我需要Concept 1工作的原因。
================================================== ==============
堆栈溢出标签
关键字的模块应该与StackOverflow标记(关键字)非常相似,如果我将TAGS设置为SQL,则正在搜索TSQL或SQL SERVER的人...也应该看到这篇文章。
:-)
解决方法
您绝对应该使用通用表表达式。这是解决您问题的理想解决方案,因为它不会更改您当前的数据库架构,而且最重要的是,由于
KeywordSynonymous
表具有递归性,CTE是一种优雅且合乎逻辑的解决方案。
为此,最好先创建一个视图,该视图在两个方向上选择KeywordSynonymous中的所有行。在您的情况下,该表返回行
select * from KeywordSynonymous
1 5
5 4
下面的视图将显示
select * from KeywordSynonymousAll
1 5 0
2 NULL 0
3 NULL 0
4 NULL 0
4 5 1
5 1 1
5 4 0
该视图是将简化递归查询的数据结构。它添加了第三列以标识何时进行了还原。这是满足您的概念编号2所必需的。
因此,这里是视图:
create view KeywordSynonymousAll as
select KeywordID,KeywordSynonymousID,0 as reversed
from KeywordSynonymous
union
select K.KeywordID,null as KeywordSynonymousID,0 as reversed
from Keywords K
where not exists(select null
from KeywordSynonymous
where KeywordID = K.KeywordID)
union
select KeywordSynonymousID,KeywordID,1 as reversed
from KeywordSynonymous
和查询
declare @search varchar(200);
set @search = \'MVC3\'; -- TEST HERE for different search keywords
with Synonymous (keywordID,SynKeywordID) as (
-- initial state: Get the keywordId and KeywordSynonymousID for the description as @search
select K.keywordID,KS.KeywordSynonymousID
from Keywords K
inner join KeywordSynonymous KS on KS.KeywordID = K.keywordId
where K.Description = @search
union all
-- also initial state but with reversed columns (because we want lookup in both directions)
select KS.KeywordSynonymousID,K.keywordID
from Keywords K
inner join KeywordSynonymous KS on KS.KeywordSynonymousID = K.keywordId
where K.Description = @search
union all
select S.SynKeywordID,KS.KeywordSynonymousID
from Synonymous S
inner join KeywordSynonymousAll KS on KS.KeywordID = S.SynKeywordID
where KS.reversed = 0 -- to avoid infinite recursion
union all
select KS.KeywordSynonymousID,S.SynKeywordID
from Synonymous S
inner join KeywordSynonymousAll KS on KS.KeywordID = S.KeywordID
where KS.reversed = 1 -- to avoid infinite recursion
)
-- finally output the result
select distinct K.Description
from Synonymous S
inner join Keywords K on K.KeywordID = S.keywordID
对于set @search = \'MVC3\'
,结果集为
ASP.NET MVC
MVC
MVC3
set @search = \'MVC\'
和set @search = \'ASP.NET MVC\'
发生相同的结果集
对于set @search = \'C#\'
和set @search = \'HTML\'
,您一无所有
编辑
在我以前的文章中,我说对于C#和HTML,结果集将为空。如果您还想返回这些值,则将查询的最后一部分更改为:
-- finally output the result
select distinct T.Description
from (
select K.Description
from Synonymous S
inner join Keywords K on K.KeywordID = S.keywordID
union
select Description
from Keywords
where Description = @search) T
现在,对于set @search = \'C#\'
,结果集为
C#
对于set @search = \'HTML\'
,结果集为
HTML
希望这可以帮助
,要达到至少#1,您可以使用递归公用表表达式(CTE)
这里的定义
,由于您的条件(概念),同义词表未规范化。这是问题的主要根源,也是解决问题所需的复杂查询/触发器。
我会保留关键字表:
CREATE TABLE [dbo].[Keywords]
[KeywordID] [int] IDENTITY(1,1) NOT NULL,[Description] [varchar](200) NOT NULL
select * from Keywords
1 MVC
2 HTML
3 C#
4 ASP.NET MVC
5 MVC3
6 C sharp
使同义表有所不同:
CREATE TABLE [dbo].[KeywordSynonymity]
[SynonymityID] [int] NOT NULL,[KeywordID] [int] NOT NULL
select * from KeywordSynonymous
1 1 --- for the 1 (MVC) and 5 (MVC3)
1 5 --- being synonymous
2 3 --- for the 3 (C#) and 6 (C sharp)
2 6 --- being synonymous
然后要加上MVC3
和ASP.NET MVC
也是同义词,您只需在“同义词”表中添加一行(1,4)。
如果这样-由于未知的原因,但尽管如此-您想将MVC3
和C#
组合为同义词,则必须将所有SynonymityID = 2(与C#相同)的行更改为= 1(与MVC相同)。
但是,随着表格的标准化,所有查询将变得更加简单。
,1称为对称关系,2称为传递关系。
我建议您在添加新关键字时对其进行维护。您可以这样操作。将关键字添加到数据库后,如果尚无同义词,则将其指定为\“ master \”关键字。否则,将新关键字链接到现有的主关键字。
这是一个以这种方式添加新关键字的存储过程:
CREATE PROCEDURE [dbo].[AddKeyword]
@newKeyword [varchar](200),@synonymKeyword [varchar](200) = NULL
AS
BEGIN
SET NOCOUNT ON;
set transaction isolation level serializable
begin transaction
if EXISTS (select 1 from Keywords where [Description] = @newKeyword)
begin
commit transaction
return
end
declare @masterKeywordId int
select
@masterKeywordId = ISNULL(KeywordSynonymous.KeywordID,Keywords.KeywordID)
from
Keywords
left join
KeywordSynonymous
on
Keywords.KeywordID = KeywordSynonymous.KeywordSynonymousID
where
[Description] = @synonymKeyword
insert into Keywords VALUES (@newKeyword)
if @masterKeywordId is not null
insert into KeywordSynonymous VALUES (@masterKeywordId,SCOPE_IDENTITY())
commit transaction
END
在此存储过程中,您传递了一个要添加的新关键字,还可以选择还传递一个已知的同义词。此同义词不必是\“ master \”。如果存在,则会查找其\“ master \”关键字ID,并将新创建的关键字与该\“ master \” ID链接起来。
这是最终选择所有它们的方式:
CREATE PROCEDURE [dbo].[GetSynonymKeywords]
@keyword [varchar](200)
AS
BEGIN
SET NOCOUNT ON;
declare @masterKeywordId int
select
@masterKeywordId = ISNULL(KeywordSynonymous.KeywordID,Keywords.KeywordID)
from
Keywords
left join
KeywordSynonymous
on
Keywords.KeywordID = KeywordSynonymous.KeywordSynonymousID
where
[Description] = @keyword
select
KeywordId,[Description]
from
Keywords
where
KeywordId = @masterKeywordId
union
select
Keywords.KeywordId,[Description]
from
KeywordSynonymous
join
Keywords
on
KeywordSynonymous.KeywordSynonymousID = Keywords.KeywordId
where
KeywordSynonymous.KeywordId = @masterKeywordId
END
该存储过程首先找到给定传递关键字的关键字ID。然后,它为该ID查找\“ master \”关键字。然后,它返回master关键字以及与此master关键字同义的所有关键字。
添加新单词的示例:
EXEC [dbo].[AddKeyword] @newKeyword = N\'MVC\'
EXEC [dbo].[AddKeyword] @newKeyword = N\'ASP.NET MVC\',@synonymKeyword = \'MVC\'
EXEC [dbo].[AddKeyword] @newKeyword = N\'MVC3\',@synonymKeyword = \'ASP.NET MVC\'
请注意,您可以在第三行中将\'MVC \'指定为同义词,它也可以正常工作。
检索关键字的示例:
[dbo].[GetSynonymKeywords] @keyword = N\'MVC3\'
[dbo].[GetSynonymKeywords] @keyword = N\'ASP.NET MVC\'
[dbo].[GetSynonymKeywords] @keyword = N\'MVC3\'
这三个都返回相同的值列表。
我在AddKeyword SP中将隔离级别序列化,以确保没有并发问题可以根据您的并发模型随意修改,序列化可能不适合您。
如果您愿意,也可以将GetMasterId(在两个SP中出现的块)拉出到UDF中,或者进行其他适合您特定情况的修改。
,好的,那这个呢?
DECLARE @TempKeywordID TABLE (KeywordID int)
INSERT INTO @TempKeywordID (KeywordID)(select KeywordID from Keywords where [Description] = @SearchKeyword)
DECLARE @intFlag INT
SET @intFlag = 1
WHILE (@intFlag <=(Select Count(KeywordSynonymousID) from KeywordSynonymous)) --Loop for all records in KeywordSynonymous
BEGIN
INSERT INTO @TempKeywordID (KeywordID)(Select KeywordSynonymousID from KeywordSynonymous where KeywordID in (Select KeywordID from @TempKeywordID))
INSERT INTO @TempKeywordID (KeywordID)(Select KeywordID from KeywordSynonymous where KeywordSynonymousID in (Select KeywordID from @TempKeywordID))
SET @intFlag = @intFlag + 1
END
SELECT * FROM Keywords WHERE KeywordID IN (SELECT * FROM @TempKeywordID)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。