以下の計算を行おうとしています
サンプルデータ:
_CREATE TABLE #Table1
(
rno int identity(1,1),
ccp varchar(50),
[col1] INT,
[col2] INT,
[col3] INT,
col4 as [col2]/100.0
);
INSERT INTO #Table1
(ccp,[col1],[col2],[col3])
VALUES ('ccp1',15,10,1100),
('ccp1',20,10,1210),
('ccp1',30,10,1331),
('ccp2',10,15,900),
('ccp2',15,15,1000),
('ccp2',20,15,1010)
+-----+------+------+------+------+----------+
| rno | ccp | col1 | col2 | col3 | col4 |
+-----+------+------+------+------+----------+
| 1 | ccp1 | 15 | 10 | 1100 | 0.100000 |
| 2 | ccp1 | 20 | 10 | 1210 | 0.100000 |
| 3 | ccp1 | 30 | 10 | 1331 | 0.100000 |
| 4 | ccp2 | 10 | 15 | 900 | 0.150000 |
| 5 | ccp2 | 15 | 15 | 1000 | 0.150000 |
| 6 | ccp2 | 20 | 15 | 1010 | 0.150000 |
+-----+------+------+------+------+----------+
_
注:これは単に_3
_レコードではなく、各ccpがN
no.ofレコードを持つことができます
期待される結果:
_1083.500000 --1100 - (15 * (1+0.100000))
1169.850000 --1210 - ((20 * (1+0.100000)) + (15 * (1+0.100000)* (1+0.100000)) )
1253.835000 --1331 - ((30 * (1+0.100000)) + (20 * (1+0.100000)* (1+0.100000)) + (15 * (1+0.100000)* (1+0.100000) *(1+0.100000)) )
888.500000 --900 - (10 * (1+0.150000))
969.525000 --1000 - ((15 * (1+0.150000)) + (10 * (1+0.150000)* (1+0.150000)) )
951.953750 --1010 - ((20 * (1+0.150000)) + (15 * (1+0.150000)* (1+0.150000)) + (10 * (1+0.150000)* (1+0.150000) *(1+0.150000)) )
_
私はこれを再帰CTEを使用して実行できることを知っています。500万以上のレコードに対してこれを実行する必要があるため、効率的ではありません。
このセットベースのアプローチのようなものを実装することを探しています
ccpの場合:_ccp1
_
_SELECT col3 - ( col1 * ( 1 + col4 ) )
FROM #Table1
WHERE rno = 1
SELECT rno,
col3 - ( ( col1 * Power(( 1 + col4 ), 1) ) + ( Lag(col1, 1)
OVER(
ORDER BY rno ) * Power(( 1 + col4 ), 2) ) )
FROM #Table1
WHERE rno IN ( 1, 2 )
SELECT rno,
col3 - ( ( col1 * Power(( 1 + col4 ), 1) ) + ( Lag(col1, 1)
OVER(
ORDER BY rno ) * Power(( 1 + col4 ), 2) ) + ( Lag(col1, 2)
OVER(
ORDER BY rno ) * Power(( 1 + col4 ), 3) ) )
FROM #Table1
WHERE rno IN ( 1, 2, 3 )
_
単一のクエリで計算する方法はありますか?
まだ提案に開いています。私は、SUM () Over(Order by)
ウィンドウ集約関数を使用してこれを実行する必要があると強く信じています。
self join
によるアプローチ。これがcross apply
のバージョンよりも効率的かどうかはわかりません。
WITH T AS
(SELECT *,
ROW_NUMBER() OVER(PARTITION BY CCP
ORDER BY RNO) AS RN
FROM #TABLE1)
SELECT T1.RNO,
T1.CCP,
T1.COL1,
T1.COL2,
T1.COL3,
T1.COL3-SUM(T2.COL1*POWER(1+T1.COL2/100.0,T1.RN-T2.RN+1)) AS RES
FROM T T1
JOIN T T2 ON T1.CCP=T2.CCP
AND T1.RN>=T2.RN
GROUP BY T1.RNO,
T1.CCP,
T1.COL1,
T1.COL2,
T1.COL3
最後に私は以下のアプローチを使用して結果を達成しました
SELECT a.*,
col3 - res AS Result
FROM #TABLE1 a
CROSS apply (SELECT Sum(b.col1 * Power(( 1 + b.COL2 / 100.00 ), new_rn)) AS res
FROM (SELECT Row_number()
OVER(
partition BY ccp
ORDER BY rno DESC) new_rn,*
FROM #TABLE1 b
WHERE a.ccp = b.ccp
AND a.rno >= b.rno)b) cs
結果:
+-----+------+------+------+------+----------+-------------+
| rno | ccp | col1 | col2 | col3 | col4 | Result |
+-----+------+------+------+------+----------+-------------+
| 1 | ccp1 | 15 | 10 | 1100 | 0.100000 | 1083.500000 |
| 2 | ccp1 | 20 | 10 | 1210 | 0.100000 | 1169.850000 |
| 3 | ccp1 | 30 | 10 | 1331 | 0.100000 | 1253.835000 |
| 4 | ccp2 | 10 | 15 | 900 | 0.150000 | 888.500000 |
| 5 | ccp2 | 15 | 15 | 1000 | 0.150000 | 969.525000 |
| 6 | ccp2 | 20 | 15 | 1010 | 0.150000 | 951.953750 |
+-----+------+------+------+------+----------+-------------+
この答えはがっかりするかもしれませんが、反復的なCLRアプローチがTSQLアプローチと競合的に機能することに気付くでしょう。
次のことを試してください( に基づいてもう一度合計を実行してください:SQLCLRは日を節約します! )
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void StackoverflowQuestion41803909()
{
using (SqlConnection conn = new SqlConnection("context connection=true;"))
{
SqlCommand comm = new SqlCommand();
comm.Connection = conn;
comm.CommandText = @"
SELECT [rno],
[ccp],
[col1],
[col2],
[col3],
[col4]
FROM Table1
ORDER BY ccp,
rno
";
SqlMetaData[] columns = new SqlMetaData[7];
columns[0] = new SqlMetaData("rno", SqlDbType.Int);
columns[1] = new SqlMetaData("ccp", SqlDbType.VarChar, 50);
columns[2] = new SqlMetaData("col1", SqlDbType.Int);
columns[3] = new SqlMetaData("col2", SqlDbType.Int);
columns[4] = new SqlMetaData("col3", SqlDbType.Int);
columns[5] = new SqlMetaData("col4", SqlDbType.Decimal, 17, 6);
columns[6] = new SqlMetaData("result", SqlDbType.Decimal, 17, 6);
SqlDataRecord record = new SqlDataRecord(columns);
SqlContext.Pipe.SendResultsStart(record);
conn.Open();
SqlDataReader reader = comm.ExecuteReader();
string prevCcp = null;
decimal offset = 0;
while (reader.Read())
{
string ccp = (string)reader[1];
int col1 = (int)reader[2];
int col3 = (int)reader[4];
decimal col4 = (decimal)reader[5];
if (prevCcp != ccp)
{
offset = 0;
}
offset = ((col1 + offset) * (1 + col4));
record.SetInt32(0, (int)reader[0]);
record.SetString(1, ccp);
record.SetInt32(2, col1);
record.SetInt32(3, (int)reader[3]);
record.SetInt32(4, col3);
record.SetDecimal(5, col4);
record.SetDecimal(6, col3 - offset);
SqlContext.Pipe.SendResultsRow(record);
prevCcp = ccp;
}
SqlContext.Pipe.SendResultsEnd();
}
}
};
別のオプション
CREATE TABLE #Table1
(
rno int identity(1,1),
ccp varchar(50),
[col1] INT,
[col2] INT,
[col3] INT,
col4 as [col2]/100.0
);
INSERT INTO #Table1
(ccp,[col1],[col2],[col3])
VALUES ('ccp1',15,10,1100),
('ccp1',20,10,1210),
('ccp1',30,10,1331),
('ccp1',40,10,1331),
('ccp2',10,15,900),
('ccp2',15,15,1000),
('ccp2',20,15,1010);
select t.*, col3-s
from(
select *, rn = row_number() over(partition by ccp order by rno)
from #Table1
) t
cross apply (
select s=sum(pwr*col1)
from(
select top(rn)
col1, pwr = power(1+col4, rn + 1 - row_number() over(order by rno))
from #Table1 t2
where t2.ccp=t.ccp
order by row_number() over(order by rno)
)t3
)t4
order by rno;
これを試して:
;with
val as (
select
*,
(1 + col2 / 100.00) val,
row_number() over(partition by ccp order by rno desc) rn
from #Table1),
res as (
select
v1.rno,
--min(v1.ccp) ccp,
--min(v1.col1) col1,
--min(v1.col2) col2,
min(v1.col3) col3,
sum(v2.col1 * power(v2.val, 1 + v2.rn - v1.rn)) sum_val
from val v1
left join val v2 on v2.ccp = v1.ccp and v2.rno <= v1.rno
group by v1.rno)
select *, col3 - isnull(sum_val, 0)
from res
ただし、パフォーマンスはインデックスに依存します。詳細については、インデックス構造を投稿してください。より一時的なテーブルに分割すると、最高のパフォーマンスが得られます。
しばらく遊んだ後、これがsum() over (order by)
で実行できるかどうかという賞金の質問に対する答えはNOだと思います。このコードは私が得ることができる限り近いです:
select *, col3 - sum(col1 * power(1 + col4, row_num)) over (partition by ccp order by col1)
from (
select *, row_number() over (partition by ccp order by rno asc) row_num
from @Table1
) a
order by 1,2;
これにより、各ccp
グループの最初の行の正しい結果が返されます。代わりにrno desc
を使用してrow_numを計算すると、各ccp
の最後の行が正しくなります。
これを構文が示唆する単純な方法で機能させる唯一の方法は次のようになると思われます:
ウィンドウ関数は、別のウィンドウ関数または集計のコンテキストでは使用できません。
これは興味深い問題でした。実際の結果が正しくない場合でも、このソリューションが大規模なデータセットに対してどのように機能するか知りたいと思います。