【问题标题】:Get the oldest name (recursively)?获取最旧的名称(递归)?
【发布时间】:2012-02-26 11:30:12
【问题描述】:

我有以下文件名更改日志表。

ChangeNameLog(Date, OldName, NewName)
主键:日期旧名称

表格的数据是这样的

日期 旧名称 新名称 1/1 aa bbb 1/2 bbb ccc 1/3 立方厘米 bbb 1/4 bbb ddd 2/1 xx 年 2/2 年年

(文件名aaa改成bbb,再改成ccc,bbb,ddd 稍后
文件名xx改为yy,再改为zz)

我想为所有新名称获取最旧的名称。结果看起来像

日期 NewName OldestName 1/2 bbb aaa 1/3 立方厘米 aaa 1/4 ddd aaa 2/1 年年月日 2/2 zz xx

不使用游标循环记录表,是否仍要编写 Transact-SQL(2008 版很好)?

以下 SQL 可用于准备数据。

declare @log table (
    Date Date, OldName varchar(20), NewName varchar(20) not null 
    primary key (Date, OldName)
);
-- The real table also have the following CK
-- create unique index IX_CK on @log (Date, NewName)

insert into @log values
 ('2012-01-01', 'aaa', 'bbb')
,('2012-01-02', 'bbb', 'ccc')
,('2012-01-03', 'ccc', 'bbb')
,('2012-01-04', 'bbb', 'ddd')
,('2012-01-05', 'ddd', 'eee')

,('2012-01-03', 'xx',  'yy')
,('2012-02-02', 'yy',  'zz')
,('2012-02-03', 'zz',  'xx')
;

【问题讨论】:

  • 我不确定,但我相当肯定您需要更具体、更深入地解释您所面临的问题才能获得任何有用的答案。跨度>
  • 看看:stackoverflow.com/questions/1757370/… 处理同样的事情(递归查询)
  • 您在日志中真的没有任何其他信息可以识别文件吗?是什么阻止您添加像 ('2012-02-04', 'xx', 'eee') 这样的行?那么接下来从'eee' -> anything else重命名,属于哪一个呢?
  • @AaronBertrand 如果已经有一个名为 eee 的文件,则不可能有像 ('2012-02-04', 'xx', 'eee') 这样的行。如果eee 不再存在,那么“Anything else”应该具有原始名称“xx”。
  • 你是如何限制这个的?当然,除了名称之外,您还有其他方法可以识别文件...

标签: sql-server sql-server-2008


【解决方案1】:

设置:

declare @logtable table (Date date, OldName nvarchar(200), NewName varchar(200))

insert into @logtable values (convert (date, '1/1/12', 1), 'aaa', 'bbb')
insert into @logtable values (convert (date, '1/2/12', 1), 'bbb', 'ccc')
insert into @logtable values (convert (date, '1/3/12', 1), 'ccc', 'bbb')
insert into @logtable values (convert (date, '1/4/12', 1), 'bbb', 'ddd')
insert into @logtable values (convert (date, '2/1/12', 1), 'xx', 'yy')
insert into @logtable values (convert (date, '2/2/12', 1), 'yy', 'zz')

现在介绍递归 CTE。第一部分(回溯)在匹配过去名称的日志表上递归,并将信息保留在链顶部(EndName)。第二部分,starters,通过changeates将行号分配给EndName,最后只显示最旧的记录。这部分可能以更多方式表达,使用 not exist on changedate,或在每个日志条目中保留原始名称,但只有当这段代码被证明太慢时,我才会研究另一种方法。

; with backtrack as (
    select NewName EndName, NewName, OldName, Date
        from @logtable
    union all
    select EndName, [@logtable].NewName, [@logtable].OldName, [@logtable].Date
    from @logtable inner join backtrack
        on [@logtable].NewName = backtrack.OldName
        and [@logtable].Date < backtrack.Date
),
starters as (
    select EndName NewName, OldName, Date, ROW_NUMBER() over (partition by EndName order by Date) RowNumber
    from backtrack
)
select NewName, OldName
from starters
where RowNumber = 1

希望对你有所帮助。

【讨论】:

    【解决方案2】:

    或者,对于另一个令人头疼的问题:

    declare @Helga as table ( Date datetime, OldName varchar(10), NewName varchar(10) )
    insert into @Helga ( Date, OldName, NewName ) values
      ( '1/1/12', 'aaa', 'bbb' ), ( '1/2/12', 'bbb', 'ccc' ), ( '1/3/12', 'ccc', 'bbb' ),
      ( '1/4/12', 'bbb', 'ddd' ), ( '2/1/12', 'xx', 'yy' ), ( '2/2/12', 'yy', 'zz' )
    select * from @Helga
    
    ; with Edmund as
    ( -- Get the oldest names.
      select L.Date, L.OldName, L.NewName, L.OldName as Methuselah, cast( 0 as bigint ) as Ethyl
        from @Helga as L left outer join
          @Helga as R on R.NewName = L.OldName
        where R.NewName is NULL
      union all
      -- Add newer names one generation at a time.
      select H.Date, H.OldName, H.NewName, H.Methuselah, H.Sandy
        from ( select iH.Date, iH.OldName, iH.NewName, Ed.Methuselah, Row_Number() over ( order by iH.Date ) as Sandy
          from Edmund as Ed cross join
            @Helga as iH where iH.OldName = Ed.NewName and iH.Date > Ed.Date ) as H
        where H.Sandy = 1
    )
    select Date, OldName, NewName, Methuselah
      from Edmund
      order by Methuselah, Date
    

    当然,如果您为每个文件分配一致的标识并在名称更改时保持不变,这将更容易、更可靠。当您有 NY > NJ > MA > CA 与 MN > MA > CA > AL 交叉路径时,所有赌注都将关闭。如果第一个序列带有FileId 1,而第二个序列全是2,您仍然可以整理出细节。

    【讨论】:

      【解决方案3】:

      我自己的解决方案:

      declare @log table (
          Date Date, OldName varchar(20), NewName varchar(20) 
          primary key (Date, OldName)
      );
      
      insert into @log values
       ('2012-01-01', 'aaa', 'bbb')
      ,('2012-01-02', 'bbb', 'ccc')
      ,('2012-01-03', 'ccc', 'bbb')
      ,('2012-01-04', 'bbb', 'ddd')
      ,('2012-01-05', 'ddd', 'eee')
      
      ,('2012-01-03', 'xx',  'yy')
      ,('2012-02-02', 'yy',  'zz')
      ,('2012-02-03', 'zz',  'xx')
      ;
      
      ;with m as (
          select Date, OldName, NewName, 1 as L
          from @log 
          union all
          select l.Date, m.OldName, l.NewName, L + 1
          from @log l join m on l.Date > m.Date and l.OldName = m.NewName
      )
      select * 
      from m
      where L = (select MAX(l) from m m1 where NewName = m.NewName and Date = m.Date)
      order by 1
      

      输出:
      下面的结果表明,原来的两个名字分别是aaaxx

      日期 原名 L 2012-01-01 aaa bbb 1 2012-01-02 aaa ccc 2 2012-01-03 aaa bbb 3 2012-01-03 xx yy 1 2012-01-04 aaa ddd 4 2012-01-05 aaa eee 5 2012-02-02 xx zz 2 2012-02-03 xx xx 3

      【讨论】:

      • 我不明白这是一个“解决方案” - 它不会像您在问题中列出的预期结果那样提供输出。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-08
      • 2016-11-17
      相关资源
      最近更新 更多