Получить самое старое имя (рекурсивно)? - PullRequest
1 голос
/ 25 февраля 2012

У меня есть следующая таблица журнала изменения имени файла.

ChangeNameLog ( Дата , OldName , NewName)
Первичный ключ: Дата , Старое имя

Данные таблицы выглядят как

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 было изменено на гг , а затем на 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 ]

3 голосов
/ 25 февраля 2012

Настройка:

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

Iнадеюсь, что это поможет вам.

0 голосов
/ 25 февраля 2012

Мое собственное решение:

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
0 голосов
/ 25 февраля 2012

Или, для другой головной боли, которая пробивается вперед:

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, вы все равно можете разобраться в деталях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...