获取最旧的名称(递归)?

时间:2012-02-24 23:41:33

标签: sql-server sql-server-2008

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

ChangeNameLog(日期 OldName ,NewName)
主键:日期 OldName

该表的数据类似于

Date OldName NewName 
1/1  aaa     bbb
1/2  bbb     ccc
1/3  ccc     bbb
1/4  bbb     ddd
2/1  xx      yy
2/2  yy      zz

(文件名 aaa 已更改为 bbb ,然后更改为 ccc bbb ddd 以后
文件名 xx 已更改为 yy ,然后更改为 zz

我想获得所有新名字的最早名称。结果看起来像

Date NewName OldestName
1/2  bbb     aaa
1/3  ccc     aaa
1/4  ddd     aaa
2/1  yy      xx
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')
;

3 个答案:

答案 0 :(得分:3)

设定:

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)上保留信息。第二部分,启动器,通过更改将行号分配给EndName,最后只显示最旧的记录。这部分可能以更多的方式表达,使用在更改时不存在,或在每个日志条目中保留原始名称,但我会调查另一种方法,只有在此代码被证明太慢时。

; 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

我希望这会对你有所帮助。

答案 1 :(得分:0)

或者,对于一种不同的头痛问题:

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&gt; NJ&gt; MA&gt;与MN的CA交叉路径> MA&gt; CA>我所有的赌注都没有了。如果第一个序列携带FileId 1而第二个序列全部为2,您仍然可以对细节进行整理。

答案 2 :(得分:0)

我自己的解决方案:

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

<强>输出:
以下结果显示两个原始名称为 aaa xx

Date       Orig Name 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