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

时间:2013-03-20 15:01:11

标签: sql sql-server string sql-server-2008

我正在开展一个名为各种药物的项目。通常,我会找到类似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字符是相同的。

e.g。 Phenytoin和Phepil将是3(他们的前三个字母是相同的)

;用x 如 ( 选择drug_name     来自rx     按药物名称分组 )

select   x.drug_name as xDrugName
        ,x2.drug_name as x2DrugName
        ,case when LEFT(x2.drug_name,6) = LEFT(x.drug_name,6) 
            then LEN(left(x.drug_name,6)) else '0' end
    from x,x as x2
    where LEFT(x2.drug_name,5) = LEFT(x.drug_name,5)
    and x.drug_name !=x2.drug_name 
    group by x.drug_name,x2.drug_name

我不需要在上面的查询中将int硬编码到左侧函数中,而是需要该整数表达式来返回两个字符串共享的相似字符数。有什么好办法吗?

2 个答案:

答案 0 :(得分:2)

这种方法使用数字生成器,然后只测试重叠的长度:

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的最高值 - 这应该是最长的子串匹配。

答案 1 :(得分:0)

你想要最长的共同序列。这是一个SQL服务器实现:

选择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