我有以下需要帮助的方案。
规则是,任何 10分钟的金额总和不能大于7(如果金额更大,则替换为零)。可以使用分析函数来完成吗?
这里是示例数据和预期结果。
with test_data as
( select 1 id , 0.2 amount,to_date('01.01.2010 18:42','DD.MM.YYYY HH24:MI') dt from dual
union all select 2 id , 0.4 amount,to_date('01.01.2010 18:58','DD.MM.YYYY HH24:MI') dt from dual
union all select 3 id , 3.0 amount,to_date('01.01.2010 18:59','DD.MM.YYYY HH24:MI') dt from dual
union all select 4 id , 0.2 amount,to_date('01.01.2010 19:00','DD.MM.YYYY HH24:MI') dt from dual
union all select 5 id , 0.2 amount,to_date('01.01.2010 19:01','DD.MM.YYYY HH24:MI') dt from dual
union all select 6 id , 0.4 amount,to_date('01.01.2010 19:02','DD.MM.YYYY HH24:MI') dt from dual
union all select 7 id , 2.6 amount,to_date('01.01.2010 19:04','DD.MM.YYYY HH24:MI') dt from dual
union all select 8 id , 0.2 amount,to_date('01.01.2010 19:05','DD.MM.YYYY HH24:MI') dt from dual
union all select 9 id , 11.2 amount,to_date('01.01.2010 19:06','DD.MM.YYYY HH24:MI') dt from dual
union all select 10 id , 10.8 amount,to_date('01.01.2010 19:08','DD.MM.YYYY HH24:MI') dt from dual
union all select 11 id , 11.4 amount,to_date('01.01.2010 19:09','DD.MM.YYYY HH24:MI') dt from dual
union all select 12 id , 6.8 amount,to_date('01.01.2010 19:18','DD.MM.YYYY HH24:MI') dt from dual
union all select 13 id , 1.8 amount,to_date('01.01.2010 19:19','DD.MM.YYYY HH24:MI') dt from dual
union all select 14 id , 1.6 amount,to_date('01.01.2010 19:21','DD.MM.YYYY HH24:MI') dt from dual
union all select 15 id , 11.4 amount,to_date('01.01.2010 19:23','DD.MM.YYYY HH24:MI') dt from dual
)
select
*
from
test_data
预期结果是:
id amount dt calculated_amount
1 0.2 1.1.2010 18:42 0.2
2 0.4 1.1.2010 18:58 0.4
3 3 1.1.2010 18:59 3
4 0.2 1.1.2010 19:00 0.2
5 0.2 1.1.2010 19:01 0.2
6 0.4 1.1.2010 19:02 0.4
7 2.6 1.1.2010 19:04 2.6
8 0.2 1.1.2010 19:05 0.2
9 11.2 1.1.2010 19:06 0
10 10.8 1.1.2010 19:08 0
11 11.4 1.1.2010 19:09 0.4
12 6.8 1.1.2010 19:18 6.6
13 1.8 1.1.2010 19:19 0
14 1.6 1.1.2010 19:21 0.4
15 11.4 1.1.2010 19:23 0
对于id = 1时间18:42增加10分钟到这个时间,你找到18:52。但是没有小于18:52的行,所以继续。 对于id = 2时间18:58增加10分钟,你找到19:08。你可以在18:58和19:08之间选择和总和。总金额是29.我们的规则被打破了,所以我们开始逐行计算2到10之间的id之和。当你达到总数大于7时,你可以把零。 (0.4 + 3 + 0.2 + 0.2 + 0.4 + 2.6 + 0.2 = 7因此id = 9且10必须为零)。并且我们必须对之前计算过的行的所有行使用此计算。 对于id = 3时间18:59添加10分钟,你找到19:09。这次,我们将之前的计算值相加(3 + 0.2 + 0.2 + 0.4 + 2.6 + 0.2 + 0 + 0 = 6.6),因此,新计算值为7-6.6 = 0.4(如果数量大于差值)
您可以在此link
中查看相关步骤答案 0 :(得分:1)
我没有完全放弃在普通SQL中可能实现的可能性,可能是通过model子句;但与此同时,这是一个PL / SQL解决方案,似乎坚持我认为规则应该是什么。无论如何,它得到了正确的答案......
create type type42 as object (id number, amount number, dt date,
calculated_amount number)
/
create type tab42 as table of type42
/
create function f42 return tab42 pipelined as
l_tab tab42;
l_sum number;
l_last_sum number;
begin
-- load the starting state into a collection; calculated amount is
-- initially just the original amount
select type42(id, amount, dt, amount)
bulk collect into l_tab
from test_data;
-- iterate over the collection row by row, each time adjusting the
-- following 10-minute's worth of rows. As the collection is being
-- updated, each iteration sees the changes from earlier ones
for i in 1..l_tab.count loop
l_sum := 0;
l_last_sum := 0;
<<inner_loop>>
-- look at all rows later than the current one
for j in i..l_tab.count loop
-- but we're only interested in the 10 minute period after the
-- starting row for this iteration (inclusive; so 19:08:00 looks
-- at rows up to 19:18:00, not 19:17:59), so exit if later
if l_tab(j).dt > l_tab(i).dt + interval '10' minute then
-- row is more than 10 minutes ahead, so stop this iteration
exit inner_loop;
end if;
-- adjust the running total based on this row's calculated amount
-- which may have been changed by previous iterations
l_sum := l_sum + l_tab(j).calculated_amount;
if l_sum >= 7 then
-- adjust the calculated amount using the last_sum value;
-- so this row gets whatever is left from the 7, regardless
-- of its actual value (but this must always be <= its amount)
l_tab(j).calculated_amount := greatest(7 - l_last_sum, 0);
end if;
-- for use on the next inner loop
l_last_sum := l_sum;
end loop;
-- send the current row with its final calculated amount
pipe row (l_tab(i));
end loop;
return;
end;
/
使用您的测试数据:
select * from table(f42);
ID AMOUNT DT CALCULATED_AMOUNT
---------- ---------- ------------------- -----------------
1 .2 2010-01-01 18:42:00 .2
2 .4 2010-01-01 18:58:00 .4
3 3 2010-01-01 18:59:00 3
4 .2 2010-01-01 19:00:00 .2
5 .2 2010-01-01 19:01:00 .2
6 .4 2010-01-01 19:02:00 .4
7 2.6 2010-01-01 19:04:00 2.6
8 .2 2010-01-01 19:05:00 .2
9 11.2 2010-01-01 19:06:00 0
10 10.8 2010-01-01 19:08:00 0
11 11.4 2010-01-01 19:09:00 .4
12 6.8 2010-01-01 19:18:00 6.6
13 1.8 2010-01-01 19:19:00 0
14 1.6 2010-01-01 19:21:00 .4
15 11.4 2010-01-01 19:23:00 0
答案 1 :(得分:0)
我不明白你的逻辑,但这个查询可能是一个起点:
WITH test_data AS
( SELECT 1 ID , 0.2 amount,TO_DATE('01.01.2010 18:42','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 2 ID , 0.4 amount,TO_DATE('01.01.2010 18:58','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 3 ID , 3.0 amount,TO_DATE('01.01.2010 18:59','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 4 ID , 0.2 amount,TO_DATE('01.01.2010 19:00','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 5 ID , 0.2 amount,TO_DATE('01.01.2010 19:01','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 6 ID , 0.4 amount,TO_DATE('01.01.2010 19:02','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 7 ID , 2.6 amount,TO_DATE('01.01.2010 19:04','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 8 ID , 0.2 amount,TO_DATE('01.01.2010 19:05','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 9 ID , 11.2 amount,TO_DATE('01.01.2010 19:06','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 10 ID , 10.8 amount,TO_DATE('01.01.2010 19:08','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 11 ID , 11.4 amount,TO_DATE('01.01.2010 19:09','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 12 ID , 6.8 amount,TO_DATE('01.01.2010 19:18','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 13 ID , 1.8 amount,TO_DATE('01.01.2010 19:19','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 14 ID , 1.6 amount,TO_DATE('01.01.2010 19:21','DD.MM.YYYY HH24:MI') dt FROM dual
UNION ALL SELECT 15 ID , 11.4 amount,TO_DATE('01.01.2010 19:23','DD.MM.YYYY HH24:MI') dt FROM dual
)
SELECT ID, amount, dt,
COUNT(*) OVER (ORDER BY dt RANGE BETWEEN CURRENT ROW AND INTERVAL '10' MINUTE FOLLOWING) AS COUNT_ROWS_PER_10_MIN,
SUM(amount) OVER (ORDER BY dt RANGE BETWEEN CURRENT ROW AND INTERVAL '10' MINUTE FOLLOWING) AS SUM_PER_10_MIN
FROM test_data
ORDER BY ID;
答案 2 :(得分:0)
这是我的答案,它对应于您的描述文字,但不是&#34;预期结果&#34;你有。无论哪种方式,我认为它很接近:
WITH TEST_DATA AS
( SELECT 1 ID , 0.2 AMOUNT,TO_DATE('01.01.2010 18:42','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 2 ID , 0.4 AMOUNT,TO_DATE('01.01.2010 18:58','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 3 ID , 3.0 AMOUNT,TO_DATE('01.01.2010 18:59','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 4 ID , 0.2 AMOUNT,TO_DATE('01.01.2010 19:00','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 5 ID , 0.2 AMOUNT,TO_DATE('01.01.2010 19:01','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 6 ID , 0.4 AMOUNT,TO_DATE('01.01.2010 19:02','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 7 ID , 2.6 AMOUNT,TO_DATE('01.01.2010 19:04','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 8 ID , 0.2 AMOUNT,TO_DATE('01.01.2010 19:05','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 9 ID , 11.2 AMOUNT,TO_DATE('01.01.2010 19:06','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 10 ID , 10.8 AMOUNT,TO_DATE('01.01.2010 19:08','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 11 ID , 11.4 AMOUNT,TO_DATE('01.01.2010 19:09','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 12 ID , 6.8 AMOUNT,TO_DATE('01.01.2010 19:18','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 13 ID , 1.8 AMOUNT,TO_DATE('01.01.2010 19:19','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 14 ID , 1.6 AMOUNT,TO_DATE('01.01.2010 19:21','DD.MM.YYYY HH24:MI') DT FROM DUAL
UNION ALL SELECT 15 ID , 11.4 AMOUNT,TO_DATE('01.01.2010 19:23','DD.MM.YYYY HH24:MI') DT FROM DUAL
)
SELECT ID
, SUM(AMOUNT)
, SUM(CASE WHEN NVL(FINAL_TOTAL, AMOUNT) != 0 THEN B_AMOUNT ELSE 0 END) AS CALC
, CASE
WHEN SUM(AMOUNT) > 7 THEN 7 - SUM(CASE WHEN NVL(FINAL_TOTAL, AMOUNT) != 0 THEN B_AMOUNT ELSE 0 END)
ELSE SUM(AMOUNT)
END AS ANSWER
FROM (
SELECT A.ID
, A.AMOUNT
, A.DT
, B.ID AS B_ID
, NVL(B.AMOUNT, A.AMOUNT) AS B_AMOUNT
, B.DT AS B_DT
, SUM(B.AMOUNT) OVER(PARTITION BY A.ID ORDER BY B.DT RANGE BETWEEN UNBOUNDED PRECEDING AND
CURRENT ROW) AS RUNNING_TOTAL
, CASE
WHEN SUM(B.AMOUNT) OVER(PARTITION BY A.ID ORDER BY B.DT RANGE BETWEEN UNBOUNDED PRECEDING AND
CURRENT ROW) > 7 THEN 0 ELSE
SUM(B.AMOUNT) OVER(PARTITION BY A.ID ORDER BY B.DT RANGE BETWEEN UNBOUNDED PRECEDING AND
CURRENT ROW) END AS FINAL_TOTAL
FROM TEST_DATA A
LEFT JOIN TEST_DATA B
ON B.DT <= A.DT + INTERVAL '10' MINUTE
AND B.DT >= A.DT
) X
WHERE NVL(FINAL_TOTAL, AMOUNT) != 0
GROUP BY ID
ORDER BY ID