我遇到的情况是我需要将查询的种子值设置为等于另一个的第一个值,并使用此值来计算下一行数据(与当前行的数据一起)。每个后续行都需要使用前面的行计算值。
我研究了窗口函数,例如OVER()
和LAG()
我似乎无法在我的情况下使用它。下面是数据样本。
最终,我需要将之前的Inventory
乘以当前行的AppliedCalc
并添加当前行的Adjustment
字段。这是将在下一行计算中使用的新Inventory
字段。
库存=之前[库存] * [AppliedCalc] + [调整]
@BeginValue
是应该提供查询的种子值。此值会更改,并将成为用于将来计算的初始库存。
@BeginValue=1,000,000
DateValue Inventory Adjustment AppliedCalc
1/31/2001 1000000 0.00 0.00
2/28/2001 1100125 125.00 1.10
3/31/2001 1133529 400.00 1.03
...
虽然我可以为SQL语句提供种子值,但我无法将计算延续到第二行之后的后续行 - 我发现我需要为每个额外的行添加一个额外的子查询。除了使用游标之外,还有解决方案吗?
答案 0 :(得分:2)
我尝试了几件事,但我无法提出 clean 解决方案。下面的解决方案是" quirky update"。这依赖于聚簇索引之后发生的更新。这没有记录,但至少对于SQL Server 2005和SQL Server 2012 SP2(我刚刚测试过),它可以工作。但是,普遍的共识是:不要使用这种生产代码的方式(尽管我从未见过 工作)。
除此之外,我认为您还可以使用CURSOR进行计算(参见第二个脚本)。这是一种保证安全的方式。
可能存在其他方法,没有奇怪的更新或使用CURSOR。但我现在无法想出一个。 HTH!
古怪的更新(使用风险自负):
CREATE TABLE #tt(
date_value DATE CONSTRAINT PK_tt_date_value PRIMARY KEY CLUSTERED,
adjustment DECIMAL(28,2),
applied_calc DECIMAL(28,2),
inventory NUMERIC(28,0) CONSTRAINT DF_tt_inventory DEFAULT(0)
);
INSERT INTO #tt(date_value,adjustment,applied_calc)
VALUES
('2001-01-31',0.00,1.00),
('2001-02-28',125.00,1.10),
('2001-03-31',400.00,1.03),
('2001-04-30',500,1.05),
('2001-05-31',100,1),
('2001-06-30',125,1.03);
DECLARE @inventory NUMERIC(28,0) = 100000;
UPDATE
t1
SET
@inventory=t1.inventory=@inventory*t1.applied_calc+t1.adjustment
FROM
#tt AS t1 WITH (INDEX=PK_tt_date_value);
SELECT
*
FROM
#tt
ORDER BY
date_value;
DROP TABLE #tt;
使用游标(安全方法):
CREATE TABLE #tt(
date_value DATE CONSTRAINT PK_tt_date_value PRIMARY KEY CLUSTERED,
adjustment DECIMAL(28,2),
applied_calc DECIMAL(28,2),
inventory NUMERIC(28,0) CONSTRAINT DF_tt_inventory DEFAULT(0)
);
INSERT INTO #tt(date_value,adjustment,applied_calc)
VALUES
('2001-01-31',0.00,1.00),
('2001-02-28',125.00,1.10),
('2001-03-31',400.00,1.03),
('2001-04-30',500,1.05),
('2001-05-31',100,1),
('2001-06-30',125,1.03);
DECLARE c_uv CURSOR
FOR SELECT adjustment,applied_calc FROM #tt ORDER BY date_value
FOR UPDATE OF inventory;
OPEN c_uv;
DECLARE @adjustment DECIMAL(28,2);
DECLARE @applied_calc DECIMAL(28,2);
DECLARE @inventory NUMERIC(28,0) = 100000;
WHILE 1=1
BEGIN
FETCH NEXT FROM
c_uv
INTO
@adjustment,
@applied_calc;
IF @@FETCH_STATUS<>0
BREAK;
SET @inventory=@inventory*@applied_calc+@adjustment;
UPDATE
#tt
SET
inventory=@inventory
WHERE
CURRENT OF c_uv;
END
CLOSE c_uv;
DEALLOCATE c_uv;
SELECT
*
FROM
#tt
ORDER BY
date_value;
DROP TABLE #tt;
两个脚本都给出以下结果:
date_value adjustment applied_calc inventory
2001-01-31 0.00 1.00 100000
2001-02-28 125.00 1.10 110125
2001-03-31 400.00 1.03 113829
2001-04-30 500.00 1.05 120020
2001-05-31 100.00 1.00 120120
2001-06-30 125.00 1.03 123849
答案 1 :(得分:1)
感谢您的帮助TT!
我尝试使用CTE来回答这个问题 - 您的建议可能是更好的选择。这就是我想出的:
CREATE TABLE #tt(
RowID INT,
date_value DATE,
Adjustment DECIMAL(28,2),
AppliedCalc DECIMAL(28,2)
);
DECLARE @BeginValue Decimal(28,2)=100000;
INSERT INTO #tt(RowID,date_value,Adjustment,AppliedCalc)
VALUES
(1,'2001-01-31',0.00,1.00),
(2,'2001-02-28',125.00,1.10),
(3,'2001-03-31',400.00,1.03),
(4,'2001-04-30',500.00,1.05),
(5,'2001-05-31',0.00,1),
(6,'2001-06-30',1.25,1.03);
;with cteOutput as (
SELECT t.RowID, date_value, isnull(t.AppliedCalc,1) as AppliedCalc, Adjustment,Cast(@BeginValue as Decimal(28,2)) as AdjMV
FROM #tt t
WHERE t.RowID = 1
UNION ALL
SELECT t.RowID, t.date_value, t.AppliedCalc, t.Adjustment,cast((t.AppliedCalc)*cte.Adjmv+isnull(t.Adjustment,0) as decimal(28,2)) as AdjMV
FROM #tt t
INNER JOIN cteOutput cte
ON t.RowID-1 = cte.RowID)
SELECT cte.RowID, cte.date_value,cte.AppliedCalc, cte.Adjustment, cte.AdjMV
FROM cteOutput cte
ORDER BY rowid
OPTION(maxrecursion 0)
DROP TABLE #tt;
和输出:
RowID date_value Inventory AppliedCalc Adjustment
1 2001-01-31 100000 1.00 0.00
2 2001-02-28 110125 1.10 125.00
3 2001-03-31 113828 1.03 400.00
4 2001-04-30 120020 1.05 500.00
5 2001-05-31 120120 1.00 100.00
6 2001-06-30 123848 1.03 125.00
答案 2 :(得分:0)
看来你正在寻找累计总数,下面是今天已经回答的使用窗口函数并在SQL 2012中工作的问题,我的答案在下面适用于所有版本
CREATE TABLE #temp (TypeA int , TypeSize int )
INSERT INTO #temp (TypeA , TypeSize)
VALUES ( 110 , 2),
( 110 , 2),
( 110 , 6),
( 200 , 5),
( 200 , 7),
( 301 , 1),
( 301 , 2),
( 301 , 5),
( 301 , 1)
---accepted answer
SELECT TypeA , TypeSize, SUM(CAST(TypeSize AS bigint))
OVER(PARTITION BY TypeA ORDER BY TypeA ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Csize FROM #temp AS A
--my answer
with cte
as
(
select *,row_number() over (partition by typea order by typea) as rn
from #temp
)
select typea,typesize,(select sum(typesize) from cte t1 where t1.rn<t2.rn+1 and t1.typea=t2.typea
group by typea
) as cszie
from cte t2