Мне нужно пересчитать количества в структуре спецификации, которая гарантирует только правильные значения количества для конечных узлов.
Если вышесказанное немного размыто, не волнуйтесь, ниже приведен упрощенный пример.
Рассмотрим иерархическую таблицу, определяющую три столбца: ID (PK), PID (родительский идентификатор в иерархии) и Q (количество - чего-либо, для текущей записи).
set nocount on
declare @results table(
ID nvarchar(3),
PID nvarchar(3),
Q int,
lvl int,
ord int
)
Столбец ord
служит только для сортировки результатов.
Столбец lvl
определяет уровень для текущей записи в иерархии.
Для тех, кому интересно, как эти столбцы поддерживаются, таблица @results заполняется в реальном мире функцией, которая выполняет все трюки; для этого примера им будут заданы жестко закодированные значения.
Теперь проблема в том, что правильные значения Q гарантируются только для узлов конечного уровня в иерархии. Для других узлов Q может быть или не быть правильно определено. Моя задача - пересчитать значения Q для этих узлов.
Пример данных:
insert into @results(ord, lvl, ID, PID, Q)
select 1, 0, 'A' as ID, null as PID, null as Q union
select 2, 1, 'B' , 'A' , 15 union
select 3, 1, 'C' , 'A' , 10 union
select 4, 2, 'B1' , 'B' , 6 union
select 5, 2, 'B2' , 'B' , 4 union
select 6, 2, 'C1' , 'C' , 5 union
select 7, 2, 'C2' , 'C' , 3 union
select 8, 3, 'C11', 'C1', 4 union
select 9, 3, 'C12', 'C1', 3
Как видите, величины для B и C1 неверны: они 15 и 5, но должны быть 10 и 7:
select * from @results order by ord
Вот исходные данные:
ID PID Q lvl ord
---- ---- ----------- ----------- -----------
A NULL NULL 0 1
B A 15 1 2
C A 10 1 3
B1 B 6 2 4
B2 B 4 2 5
C1 C 5 2 6
C2 C 3 2 7
C11 C1 4 3 8
C12 C1 3 3 9
Наконец, вопрос: есть ли способ обновить эту таблицу на основе набора, чтобы все количества обновлялись суммированными количествами дочерних узлов снизу вверх?
Лучшее, с чем я пришел, можно увидеть ниже, но оно не основано на:
declare @level int
select @level = max(lvl) from @results
while (@level > 0)
begin
update r set Q = s.SumQ
from @results r inner join (
select PID, sum(Q) as SumQ
from @results
where lvl = @level group by PID
) s on ( r.ID = s.PID )
set @level = @level - 1
end
select * from @results
, что дает правильные количества:
ID PID Q lvl ord
---- ---- ----------- ----------- -----------
A NULL 20 0 1
B A 10 1 2
C A 10 1 3
B1 B 6 2 4
B2 B 4 2 5
C1 C 7 2 6
C2 C 3 2 7
C11 C1 4 3 8
C12 C1 3 3 9
Спасибо!