如何获取以前计算的行结果

时间:2015-03-04 11:47:26

标签: sql oracle analytics

我有以下需要帮助的方案。

规则是,任何 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

中查看相关步骤

3 个答案:

答案 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