Sql 按相似性顺序查找至少共享X个字符的所有字符串
我正在做一个有各种药物名称的项目。通常,我会找到类似Proscratinol和Proscratinol XR的扩展版本。我想找到一个查询来提取所有这种性质的名称,这样我就可以把“父”药物放在一个表中,让这些“子”药物引用它,所以当我写一个查询来进行药物计数时,我不会重复计算Proscratinol,因为它有一个XR、CR和任何其他版本。我写了下面这篇文章是为了尝试一下Sql 按相似性顺序查找至少共享X个字符的所有字符串,sql,sql-server,string,sql-server-2008,Sql,Sql Server,String,Sql Server 2008,我正在做一个有各种药物名称的项目。通常,我会找到类似Proscratinol和Proscratinol XR的扩展版本。我想找到一个查询来提取所有这种性质的名称,这样我就可以把“父”药物放在一个表中,让这些“子”药物引用它,所以当我写一个查询来进行药物计数时,我不会重复计算Proscratinol,因为它有一个XR、CR和任何其他版本。我写了下面这篇文章是为了尝试一下 ;with x as ( select drug_name from rx group by drug_na
;with x
as
(
select drug_name
from rx
group by drug_name
)
select distinct *
from x,x as x2
where LEFT(x2.drug_name,5) = LEFT(x.drug_name,5)
and x.drug_name !=x2.drug_name
这将给我一份名单,列出所有名字前五个字母相同的药物。在这里,五是完全任意的。到目前为止,我所得到的已经足够好了,但是我想按相似性的降序排列结果。所以我想找出他们从左边读的X-most字符是一样的
e、 苯妥英钠和Phepil是3,它们的前三个字母是相同的
);与x
像
选择药物名称
从rx
按药物名称分组
在上面的查询中,我需要整数表达式来返回两个字符串共享的相似字符数,而不是将int硬编码到left函数中。有什么好办法吗?这种方法使用数字生成器,然后只测试重叠的长度:
select x.drug_name, x2.drug_name, MAX(c.seqnum) as OverlapLen
from x cross join
x x2 cross join
(select ROW_NUMBER() over (order by (select NULL)) seqnum
from INFORMATION_SCHEMA.COLUMNS c
) c
where LEFT(x.drug_name, c.seqnum) = LEFT(x2.drug_name, c.seqnum) and
len(x.drug_name) >= c.seqnum and len(x2.drug_name) >= c.seqnum
group by x.drug_name, x.drug_name
order by x.drug_name, OverlapLen desc
这假设information_schema.columns有足够的行来存放较长的药物名称
它将x连接到自身,然后连接到一个数字列表中。where子句检查三个条件:1每个药品名称的左半部分在seqnum之前相同;2每个药品名称的长度小于或等于seqnum
然后,聚合取每对并选择seqnum的最大值-这应该是最长的子字符串匹配。您想要最长的公共序列。以下是SQL server实现: 选择dbo。lcs@string1,@string2,len@string1, len@string2
CREATE FUNCTION [dbo].[LCS]( @s varchar(MAX), @t varchar(MAX) )
RETURNS INT AS
BEGIN
DECLARE @d varchar(MAX), @LD INT, @m INT, @n INT, @i INT, @j INT,
@s_i NCHAR(1), @t_j NCHAR(1)
SET @n = LEN(@s)
IF @n = 0 RETURN 0
SET @m = LEN(@t)
IF @m = 0 RETURN 0
SET @d = REPLICATE(CHAR(0),(@n+1)*(@m+1))
SET @i = 1
WHILE @i <= @n BEGIN
SET @s_i = SUBSTRING(@s,@i,1)
SET @j = 1
WHILE @j <= @m BEGIN
SET @t_j = SUBSTRING(@t,@j,1)
IF @s_i = @t_j
SET @d = STUFF(@d,@j*(@n+1)+@i+1,1,
NCHAR(UNICODE(
SUBSTRING(@d, (@j-1)*(@n+1)+@i-1+1, 1)
)+1))
ELSE
SET @d = STUFF(@d,@j*(@n+1)+@i+1,1,CHAR(dbo.Max2(
UNICODE(SUBSTRING(@d,@j*(@n+1)+@i-1+1,1)),
UNICODE(SUBSTRING(@d,(@j-1)*(@n+1)+@i+1,1)))))
SET @j = @j+1
END
SET @i = @i+1
END
SET @LD = UNICODE(SUBSTRING(@d,@n*(@m+1)+@m+1,1))
RETURN @LD
END
我无法回答您的问题,但请在代码中使用显式联接。隐式联接已被弃用,可能会产生不必要的结果,如意外的交叉联接。请注意,在本例中,我使用了交叉联接,因为它速度更快,而且意图并不模糊。您可以尝试查看全文索引和查询。我对它的了解还不够确定,但我认为它比你要求的更符合你的目的。我有机会测试了一下,但它不起作用,每个seqnum都是一样的。你能解释一下你在这里想做什么吗?@wootscootinboogie。我把长度比较倒过来了。我在别的地方问了同样的问题,这个解决方案是目前为止最好的。布拉沃:你应该得到不止一张选票
CREATE FUNCTION [dbo].[LCS]( @s varchar(MAX), @t varchar(MAX) )
RETURNS INT AS
BEGIN
DECLARE @d varchar(MAX), @LD INT, @m INT, @n INT, @i INT, @j INT,
@s_i NCHAR(1), @t_j NCHAR(1)
SET @n = LEN(@s)
IF @n = 0 RETURN 0
SET @m = LEN(@t)
IF @m = 0 RETURN 0
SET @d = REPLICATE(CHAR(0),(@n+1)*(@m+1))
SET @i = 1
WHILE @i <= @n BEGIN
SET @s_i = SUBSTRING(@s,@i,1)
SET @j = 1
WHILE @j <= @m BEGIN
SET @t_j = SUBSTRING(@t,@j,1)
IF @s_i = @t_j
SET @d = STUFF(@d,@j*(@n+1)+@i+1,1,
NCHAR(UNICODE(
SUBSTRING(@d, (@j-1)*(@n+1)+@i-1+1, 1)
)+1))
ELSE
SET @d = STUFF(@d,@j*(@n+1)+@i+1,1,CHAR(dbo.Max2(
UNICODE(SUBSTRING(@d,@j*(@n+1)+@i-1+1,1)),
UNICODE(SUBSTRING(@d,(@j-1)*(@n+1)+@i+1,1)))))
SET @j = @j+1
END
SET @i = @i+1
END
SET @LD = UNICODE(SUBSTRING(@d,@n*(@m+1)+@m+1,1))
RETURN @LD
END