Чтобы избежать циклов и вставки копий, вам понадобится dynamic pivot
для двух агрегированных значений, подобных этому:
DECLARE
@cmd NVARCHAR(MAX) = N'',
@qty_cols NVARCHAR(MAX) = N'',
@val_cols NVARCHAR(MAX) = N'',
@n int = 0
-- this can be done in any other manner like via spt_values or FOR XML and so on
SELECT TOP 100 PERCENT
@n += 1,
@qty_cols += ', [rateqty-' + CAST(@n AS VARCHAR(10)) + ']',
@val_cols += ', [rateval-' + CAST(@n AS VARCHAR(10)) + ']',
@cmd += ', MAX([rateqty-' + CAST(@n AS VARCHAR(10)) + ']) as [rateqty-' + CAST(@n AS VARCHAR(10)) + ']'
+ ', MAX([rateval-' + CAST(@n AS VARCHAR(10)) + ']) as [rateval-' + CAST(@n AS VARCHAR(10)) + ']'
FROM #rates r
ORDER BY r.ratecode
set @cmd = 'SELECT ' + STUFF(@cmd, 1, 2, '') + '
from
(
select
''rateqty-'' + CAST(rn AS VARCHAR(10)) ratecode_qty,
''rateval-'' + CAST(rn AS VARCHAR(10)) ratecode_val,
rateqty, rateval
from (
select ratecode, rateqty, rateval, ROW_NUMBER() OVER(ORDER BY r.ratecode) rn
from #rates r
) r
) r
pivot (max(r.rateqty) for ratecode_qty in (' + STUFF(@qty_cols, 1, 2, '') + ' )) p1
pivot (max(p1.rateval) for ratecode_val in (' + STUFF(@val_cols, 1, 2, '') + ' )) p2
order by 1'
exec(@cmd)
;
Для этих образцов данных:
insert into rates(ratecode)
values (1), (2), (3)
insert into solditems(ratecode, qty, price)
values
(1, 5, 22),
(1, 2, 22),
(3, 1, 33)
вывод будет:
| rateqty-1 | rateval-1 | rateqty-2 | rateval-2 | rateqty-3 | rateval-3 |
|-----------|-----------|-----------|-----------|-----------|-----------|
| 2 | 154 | 0 | 0 | 1 | 33 |
Полный источник здесь:
http://sqlfiddle.com/#!18/6f390/42
Что касается вопроса об упрощении ваших подзапросов в отчете, где больше исходных таблиц, чем я предполагаю (так как вы показали только часть кода), то ratecodes
должен быть CROSS JOIN
ed для внешнего запроса (который звучит немного пугающе) или присоединяется к solditems
напрямую и агрегируется (, а затем поворачивается) так:
SELECT
...
FROM ... <outer query>
CROSS APPLY
(
select
r.ratecode,
count(1) rateqty, -- i don't quite understand what is this supposed to be; number of transactions?
sum(si.qty * si.price) rateval
from solditems si
INNER JOIN dbo.rates r
ON si.ratecode = r.ratecode
where si.itemno = s.itemno
group by r.ratecode
) s
upd: после публикации этого подзапроса я понял, что dbo.rates
нигде не используется, кроме ROWNUMBER
. Поэтому, если вы делаете поворот, здесь не требуется присоединение к rates
:
SELECT
...
FROM ... <outer query>
CROSS APPLY
(
select
si.ratecode,
count(1) rateqty,
sum(si.qty * si.price) rateval
from solditems si
where si.itemno = s.itemno
group by si.ratecode
) s