web-dev-qa-db-ja.com

特定の条件が満たされるまでの累計

積算合計について質問があります。いくつかのアプローチがあることは知っています。しかし、私はそれを少し変更し、正しい方法を見つけるのに苦労しています。

したがって、注文があります。各注文には一意のIDと数量があります。数量は増加(購入)および減少(販売)できます。 IDは順序どおりに進みますが、「密」ではないため、2つの後続の順序の間にギャップがある場合があります。最も古いポジションを表すすべての注文を選択する必要があります。位置は連続した注文の範囲であり、それらの合計は0です。例(id、qty):(1、1)(2,2)(3、-3)(4,1)(5、-1)- 2つのポジションがあり、最も古いポジションはID 1、2、3の注文で構成されます。

私の現在のアプローチ:

スキーマ

CREATE TABLE [dbo].[orders](
    [id] [int] NULL,
    [qty] [int] NULL
) ON [PRIMARY]

解決

create table #or (id int, qty int, rn int)
create clustered index ix_orid on #or(rn)
insert into #or
select *, ROW_NUMBER() over(order by id) as rn from dbo.orders;

with position as (
select o1.*, o1.qty as s from #or o1 where rn = 1
union all
select o1.id, o1.qty, o1.rn, o1.qty + position.s   
from #or o1
join position
on o1.rn = position.rn + 1
where position.s > 0
)
select * from position 
option (maxrecursion 0)

drop table #or

私の懸念は、すべての注文に対して大きな一時テーブルを作成することです。そして、このテーブルの作成には常に時間がかかります。 dbo.ordersに約3.000.000行あり、約15.000が結果セットです。現在のアイデアは、#への挿入を制限するか、トップヒントを適切な量で使用し、完了するまで数回実行することです。でももっと簡単にできるかな?

1
Irdis

コメントで提案されているように、現在の合計がゼロになるのを監視することで、注文の最新のIDを1つの位置で取得できます。次に、範囲内の注文を選択できます

declare @latestId int;
WITH T1
     AS (SELECT *,
                SUM(qty) OVER (ORDER BY id ROWS UNBOUNDED PRECEDING) AS total
         FROM   [dbo].[orders])
SELECT top (1) @latestId = id
FROM   T1
WHERE  total <= 0
order by id

select * from [dbo].[orders] where 1 <= id and id <= @latestId
0
Irdis