我有以下文件名更改日志表。
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')
;
答案 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