TSQL 根据同一列中前一行的值计算行值
TSQL calculate row value based on value in previous row in same column
我有一个数据集,我需要为每一行计算一个值,该值取决于同一列的前一行中的值。或者当没有上一行时最初为 1。我需要在不同的分区上执行此操作。
公式如下:factor =(前一个factor,如果不存在则为1)*(1 + div / nav)
这需要按 Inst_id.
进行分区
我宁愿避免游标。也许用递归 cte - 但我无法理解它 - 或者其他方式?
我知道这段代码不起作用,因为我无法引用同一列,但这是显示我正在尝试做的事情的另一种方式:
SELECT Dato, Inst_id, nav, div
, (1 + div / nav ) * ISNULL(LAG(factor, 1) OVER (PARTITION BY Inst_id ORDER BY Date), 1) AS factor
FROM @tmp
因此,对于我的测试数据,我需要在下面的因子列中获得这些结果。
请忽略四舍五入问题,因为我在 Excel:
中进行了计算
date Inst_id nav div factor
11-04-2012 16 57.5700 5.7500 1.09987841
19-04-2013 16 102.8600 10.2500 1.20948130
29-04-2014 16 65.9300 16.7500 1.51675890
08-04-2013 29 111.2736 17.2500 1.15502333
10-04-2014 29 101.9650 16.3000 1.33966395
15-04-2015 29 109.5400 7.5000 1.43138825
27-04-2016 29 94.2500 0.4000 1.43746311
15-04-2015 34 159.1300 11.4000 1.07163954
27-04-2016 34 124.6100 17.6000 1.22299863
26-04-2017 34 139.7900 9.2000 1.30348784
01-04-2016 38 99.4600 0.1000 1.00100543
26-04-2017 38 102.9200 2.1000 1.02143014
测试数据:
DECLARE @tmp TABLE(Dato DATE, Inst_id INT, nav DECIMAL(26,19), div DECIMAL(26,19), factor DECIMAL(26,19))
INSERT INTO @tmp (Dato, Inst_id, nav, div) VALUES
('2012-04-11', 16, 57.57, 5.75),
('2013-04-19', 16, 102.86, 10.25),
('2014-04-29', 16, 65.93, 16.75),
('2013-04-08', 29, 111.273577, 17.25),
('2014-04-10', 29, 101.964994, 16.3),
('2015-04-15', 29, 109.54, 7.5),
('2016-04-27', 29, 94.25, 0.4),
('2015-04-15', 34, 159.13, 11.4),
('2016-04-27', 34, 124.61, 17.6),
('2017-04-26', 34, 139.79, 9.2)
我使用的是 Microsoft SQL Server Enterprise 2016(并使用 SSMS 2016)。
使用递归 CTE:
WITH DataSource AS
(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY Inst_id ORDER BY Dato) AS [rowId]
FROM @tmp
),
RecursiveDataSource AS
(
SELECT *
,CAST((1 + div / nav ) * 1 AS DECIMAL(26,19)) as [factor_calculated]
FROM DataSource
WHERE [rowId] = 1
UNION ALL
SELECT A.*
,CAST((1 + A.div / A.nav ) * R.factor_calculated AS DECIMAL(26,19)) as [factor_calculated]
FROM RecursiveDataSource R
INNER JOIN DataSource A
ON r.[Inst_id] = A.[Inst_id]
AND R.[rowId] + 1 = A.[rowId]
)
SELECT *
FROM RecursiveDataSource
ORDER BY Inst_id, Dato;
我猜你在第 3 行之后的 Excel 中得到了不同的值,因为你没有在那里按 Inst_id
进行分区。
您可以使用(如果 DIV 和 NAV 总是 >0):
SELECT A.* , EXP(SUM( LOG(1+DIV/NAV) ) OVER (PARTITION BY INST_ID ORDER BY DATO) )AS FACT_NEW
FROM @tmp A
实际上你需要的是聚合函数 MULTIPLY() OVER ... 的等价物。
使用对数定理:LOG(M*N) = LOG(M) + LOG (N) 你可以做到;例如:
DECLARE @X1 NUMERIC(10,4)=5
DECLARE @X2 NUMERIC(10,4)=7
SELECT @x1*@x2 AS S1, EXP(LOG(@X1)+LOG(@X2)) AS S2
输出:
+------------+---------+-------------------------+------------------------+--------+------------------+
| Dato | Inst_id | nav | div | factor | FACT_NEW |
+------------+---------+-------------------------+------------------------+--------+------------------+
| 2012-04-11 | 16 | 57.5700000000000000000 | 5.7500000000000000000 | NULL | 1.099878408893 |
| 2013-04-19 | 16 | 102.8600000000000000000 | 10.2500000000000000000 | NULL | 1.20948130303111 |
| 2014-04-29 | 16 | 65.9300000000000000000 | 16.7500000000000000000 | NULL | 1.51675889783963 |
| 2013-04-08 | 29 | 111.2735770000000000000 | 17.2500000000000000000 | NULL | 1.155023325977 |
| 2014-04-10 | 29 | 101.9649940000000000000 | 16.3000000000000000000 | NULL | 1.33966395090911 |
| 2015-04-15 | 29 | 109.5400000000000000000 | 7.5000000000000000000 | NULL | 1.43138824917236 |
| 2016-04-27 | 29 | 94.2500000000000000000 | 0.4000000000000000000 | NULL | 1.43746310646293 |
| 2015-04-15 | 34 | 159.1300000000000000000 | 11.4000000000000000000 | NULL | 1.071639539998 |
| 2016-04-27 | 34 | 124.6100000000000000000 | 17.6000000000000000000 | NULL | 1.22299862758278 |
| 2017-04-26 | 34 | 139.7900000000000000000 | 9.2000000000000000000 | NULL | 1.30348784264639 |
+------------+---------+-------------------------+------------------------+--------+------------------+
我有一个数据集,我需要为每一行计算一个值,该值取决于同一列的前一行中的值。或者当没有上一行时最初为 1。我需要在不同的分区上执行此操作。
公式如下:factor =(前一个factor,如果不存在则为1)*(1 + div / nav) 这需要按 Inst_id.
进行分区我宁愿避免游标。也许用递归 cte - 但我无法理解它 - 或者其他方式?
我知道这段代码不起作用,因为我无法引用同一列,但这是显示我正在尝试做的事情的另一种方式:
SELECT Dato, Inst_id, nav, div
, (1 + div / nav ) * ISNULL(LAG(factor, 1) OVER (PARTITION BY Inst_id ORDER BY Date), 1) AS factor
FROM @tmp
因此,对于我的测试数据,我需要在下面的因子列中获得这些结果。 请忽略四舍五入问题,因为我在 Excel:
中进行了计算date Inst_id nav div factor
11-04-2012 16 57.5700 5.7500 1.09987841
19-04-2013 16 102.8600 10.2500 1.20948130
29-04-2014 16 65.9300 16.7500 1.51675890
08-04-2013 29 111.2736 17.2500 1.15502333
10-04-2014 29 101.9650 16.3000 1.33966395
15-04-2015 29 109.5400 7.5000 1.43138825
27-04-2016 29 94.2500 0.4000 1.43746311
15-04-2015 34 159.1300 11.4000 1.07163954
27-04-2016 34 124.6100 17.6000 1.22299863
26-04-2017 34 139.7900 9.2000 1.30348784
01-04-2016 38 99.4600 0.1000 1.00100543
26-04-2017 38 102.9200 2.1000 1.02143014
测试数据:
DECLARE @tmp TABLE(Dato DATE, Inst_id INT, nav DECIMAL(26,19), div DECIMAL(26,19), factor DECIMAL(26,19))
INSERT INTO @tmp (Dato, Inst_id, nav, div) VALUES
('2012-04-11', 16, 57.57, 5.75),
('2013-04-19', 16, 102.86, 10.25),
('2014-04-29', 16, 65.93, 16.75),
('2013-04-08', 29, 111.273577, 17.25),
('2014-04-10', 29, 101.964994, 16.3),
('2015-04-15', 29, 109.54, 7.5),
('2016-04-27', 29, 94.25, 0.4),
('2015-04-15', 34, 159.13, 11.4),
('2016-04-27', 34, 124.61, 17.6),
('2017-04-26', 34, 139.79, 9.2)
我使用的是 Microsoft SQL Server Enterprise 2016(并使用 SSMS 2016)。
使用递归 CTE:
WITH DataSource AS
(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY Inst_id ORDER BY Dato) AS [rowId]
FROM @tmp
),
RecursiveDataSource AS
(
SELECT *
,CAST((1 + div / nav ) * 1 AS DECIMAL(26,19)) as [factor_calculated]
FROM DataSource
WHERE [rowId] = 1
UNION ALL
SELECT A.*
,CAST((1 + A.div / A.nav ) * R.factor_calculated AS DECIMAL(26,19)) as [factor_calculated]
FROM RecursiveDataSource R
INNER JOIN DataSource A
ON r.[Inst_id] = A.[Inst_id]
AND R.[rowId] + 1 = A.[rowId]
)
SELECT *
FROM RecursiveDataSource
ORDER BY Inst_id, Dato;
我猜你在第 3 行之后的 Excel 中得到了不同的值,因为你没有在那里按 Inst_id
进行分区。
您可以使用(如果 DIV 和 NAV 总是 >0):
SELECT A.* , EXP(SUM( LOG(1+DIV/NAV) ) OVER (PARTITION BY INST_ID ORDER BY DATO) )AS FACT_NEW
FROM @tmp A
实际上你需要的是聚合函数 MULTIPLY() OVER ... 的等价物。 使用对数定理:LOG(M*N) = LOG(M) + LOG (N) 你可以做到;例如:
DECLARE @X1 NUMERIC(10,4)=5
DECLARE @X2 NUMERIC(10,4)=7
SELECT @x1*@x2 AS S1, EXP(LOG(@X1)+LOG(@X2)) AS S2
输出:
+------------+---------+-------------------------+------------------------+--------+------------------+
| Dato | Inst_id | nav | div | factor | FACT_NEW |
+------------+---------+-------------------------+------------------------+--------+------------------+
| 2012-04-11 | 16 | 57.5700000000000000000 | 5.7500000000000000000 | NULL | 1.099878408893 |
| 2013-04-19 | 16 | 102.8600000000000000000 | 10.2500000000000000000 | NULL | 1.20948130303111 |
| 2014-04-29 | 16 | 65.9300000000000000000 | 16.7500000000000000000 | NULL | 1.51675889783963 |
| 2013-04-08 | 29 | 111.2735770000000000000 | 17.2500000000000000000 | NULL | 1.155023325977 |
| 2014-04-10 | 29 | 101.9649940000000000000 | 16.3000000000000000000 | NULL | 1.33966395090911 |
| 2015-04-15 | 29 | 109.5400000000000000000 | 7.5000000000000000000 | NULL | 1.43138824917236 |
| 2016-04-27 | 29 | 94.2500000000000000000 | 0.4000000000000000000 | NULL | 1.43746310646293 |
| 2015-04-15 | 34 | 159.1300000000000000000 | 11.4000000000000000000 | NULL | 1.071639539998 |
| 2016-04-27 | 34 | 124.6100000000000000000 | 17.6000000000000000000 | NULL | 1.22299862758278 |
| 2017-04-26 | 34 | 139.7900000000000000000 | 9.2000000000000000000 | NULL | 1.30348784264639 |
+------------+---------+-------------------------+------------------------+--------+------------------+