4

我正在从事一个具有各种药物名称的项目。通常,我会找到类似 Proscratinol 和 Proscratinol XR(延长版)的东西。我想找到一个查询来获取所有这种性质的名称,这样我就可以将“父”药物放在一个表中并让这些“子”药物引用它,所以当我编写查询来进行药物计数时,我不要重复计算前列素,因为它有 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 字符是相同的。

例如 Phenytoin 和 Phepil 为 3(它们的前三个字母相同)

;with x as (select drug_name from rx group by drug_name )

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 硬编码到 left 函数中,而是需要该整数表达式来返回两个字符串共享多少相似字符。有什么好方法可以做到这一点?

4

2 回答 2

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

于 2013-03-20T15:40:09.347 回答
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
于 2013-07-01T19:50:29.160 回答