Sql 按相似性顺序查找至少共享X个字符的所有字符串

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

我正在做一个有各种药物名称的项目。通常,我会找到类似Proscratinol和Proscratinol XR的扩展版本。我想找到一个查询来提取所有这种性质的名称,这样我就可以把“父”药物放在一个表中,让这些“子”药物引用它,所以当我写一个查询来进行药物计数时,我不会重复计算Proscratinol,因为它有一个XR、CR和任何其他版本。我写了下面这篇文章是为了尝试一下

;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