Home » SQL & PL/SQL » SQL & PL/SQL » Oracle sql query to pick current and previous records (Oracle, 11g, Windows)
Oracle sql query to pick current and previous records [message #622880] Mon, 01 September 2014 06:24 Go to next message
surestce
Messages: 2
Registered: September 2014
Junior Member
have a table that contains all transactions with Quantity column. Need to calculate the cumulative total of quantity of transaction group and update in each transaction row.

TRANSACTION table:
id(PK) group_id, txn_id, txn_date, Quantity, cumulative_qty 
1 1 111 12-AUG-14 10 10
2 1 112 13-AUG-14 -5 5 
3 1 113 14-AUG-14 2 7
4 2 221 23-AUG-14 15 15 
5 2 222 23-AUG-14 15 30

I wrote MERGE INTO script and it is working file. But the problem is that it is updating old records also. This table is having huge volume of data and that hit is performance issue.

Now I need a merge script that need to fetch only unprocessed new records (cumulative_qty is NULL) and add the Quantity with last cumulative_qty value.

Suppose, if there is any new transactions for the same group, the select of merge should pick up old and new records alone and update the new record.
id(PK) group_id, txn_id, txn_date, Quantity, cumulative_qty 
6 1 114 25-AUG-14 5 NULL
7 1 115 26-AUG-14 2 NULL
8 2 223 25-AUG-14 -10 NULL 

merge into
txn t1
using (
select
txn_id,
lag(cumulative_qty, 1) over (
partition by group_id 
order by txn_id
) as prev_cum_qty
from
txn
) x
on
(t1.txn_id = x.txn_id)
when matched then update set
cumulative_qty = quantity + prev_cum_qty
where
cumulative_qty is null;

In the above merge query, the select query returns all 7 rows to process and then merge 2 rows. But I want the select query that should return only 4 rows (id: 3,5,6,7,Cool i.e. the last cumulative row (order by max date and id) and unprocessed rows only.

I am using Oracle 11g.

Please help me. Thanks in advance.


Lalit : Added code tags, but unable to format the code.
LF: fixed topic title typo

[Updated on: Mon, 01 September 2014 06:39] by Moderator

Report message to a moderator

Re: Oralce sql query to pick current and previous records [message #622881 is a reply to message #622880] Mon, 01 September 2014 06:32 Go to previous messageGo to next message
Lalit Kumar B
Messages: 3174
Registered: May 2013
Location: World Wide on the Web
Senior Member
Welcome to the forum!

Please read and follow the OraFAQ Forum Guide and How to use [code] tags, to enable us to help you.

1. Request you to please format the part which I have added code tags.
2. Please provide a working test case. Understand, we don't have your tables and data.
3. Show us using copy paste what you did and what you expect from us.
4. Read the two links posted above.

Please do the above and post the required details. And please format your code and use code tags when you post again.

[Updated on: Mon, 01 September 2014 07:01] by Moderator

Report message to a moderator

Re: Oracle sql query to pick current and previous records [message #622890 is a reply to message #622880] Mon, 01 September 2014 06:59 Go to previous messageGo to next message
surestce
Messages: 2
Registered: September 2014
Junior Member
Hi,

Script to create a table and insert record:

create table txn (
  txn_id numeric(9) not null constraint PK_txn primary key,
  group_id numeric(9),
  txn_date date,
  quantity numeric(9),
  cumulative_qty numeric(9)
);

create index ix_group_txn on txn(group_id, txn_id);

insert into txn values (1, 1, DATE'2014-08-12', 10, 10);
insert into txn values (2, 1, DATE'2014-08-13', -5, 5);
insert into txn values (3, 1, DATE'2014-08-14', 2, 7);
insert into txn values (4, 2, DATE'2014-08-23', 15, 15);
insert into txn values (5, 2, DATE'2014-08-23', 15, 30);
insert into txn values (6, 1, DATE'2014-08-25', 5, NULL);
insert into txn values (7, 1, DATE'2014-08-26', 2, NULL);
insert into txn values (8, 2, DATE'2014-08-25', -10, NULL);
insert into txn values (9, 3, DATE'2014-08-28', 100, NULL);


Table having processed Records:
id(PK) group_id, txn_id, txn_date,   Quantity, cumulative_qty    
  1       1      111     12-AUG-14     10            10
  2       1      112     13-AUG-14     -5             5  
  3       1      113     14-AUG-14      2             7
  4       2      221     23-AUG-14     15            15   
  5       2      222     23-AUG-14     15            30


Table refreshed with unprocessed Records:
id(PK) group_id, txn_id, txn_date,   Quantity, cumulative_qty    
  1       1      111     12-AUG-14     10            10
  2       1      112     13-AUG-14     -5             5  
  3       1      113     14-AUG-14      2             7
  4       2      221     23-AUG-14     15            15   
  5       2      222     23-AUG-14     15            30
  6       1      114     26-AUG-14      5           NULL
  7       1      115     27-AUG-14      2           NULL
  8       2      223     28-AUG-14     -10          NULL
  9       3      333     29-AUG-14     125          NULL


Current Merge script to update unprocessed records with cumulative value:

MERGE INTO txn t1 USING
(SELECT txn_id,
  lag(cumulative_qty, 1) over ( partition BY group_id order by txn_id ) AS prev_cum_qty
FROM txn
) x ON (t1.txn_id = x.txn_id)
WHEN matched THEN
  UPDATE
  SET cumulative_qty    = quantity + prev_cum_qty
  WHERE cumulative_qty IS NULL;



In the above merge query, the select query fetching all the rows (in this case all 9 records) and merge 4 records. This hitting performance issue. I want the select query to return only the unprocessed rows(txn_id: 6,7,8,9) and latest processed rows(txn_id: 3,5).

SELECT txn_id,
  lag(cumulative_qty, 1) over ( partition BY group_id order by txn_id ) AS prev_cum_qty
FROM txn;


[Updated on: Mon, 01 September 2014 07:04]

Report message to a moderator

Re: Oracle sql query to pick current and previous records [message #622893 is a reply to message #622890] Mon, 01 September 2014 07:47 Go to previous messageGo to next message
Michel Cadot
Messages: 68645
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator

Here's a starter:
SQL> select * from txn order by group_id, txn_id;
    TXN_ID   GROUP_ID TXN_DATE      QUANTITY CUMULATIVE_QTY
---------- ---------- ----------- ---------- --------------
         1          1 12-AUG-2014         10             10
         2          1 13-AUG-2014         -5              5
         3          1 14-AUG-2014          2              7
         6          1 25-AUG-2014          5
         7          1 26-AUG-2014          2
         4          2 23-AUG-2014         15             15
         5          2 23-AUG-2014         15             30
         8          2 25-AUG-2014        -10
         9          3 28-AUG-2014        100

9 rows selected.

SQL> merge
  2    into txn t
  3    using (with
  4             maxtxn as (
  5               select group_id, max(txn_id) maxtxn
  6               from txn
  7               where cumulative_qty is not null
  8               group by group_id
  9             )
 10           select t.group_id, t.txn_id, t.quantity,
 11                  last_value (t.cumulative_qty ignore nulls)
 12                    over (partition by t.group_id order by t.txn_id)
 13                  + sum(decode(t.txn_id,m.maxtxn,0,t.quantity)) over
 14                      (partition by t.group_id
 15                       order by t.txn_id
 16                       rows between unbounded preceding and current row)
 17                    cumulative_qty
 18           from txn t, maxtxn m
 19           where t.group_id = m.group_id
 20             and t.txn_id >= m.maxtxn) x
 21    on (t.txn_id = x.txn_id)
 22  when matched then
 23    update set cumulative_qty = nvl(x.cumulative_qty, x.quantity)
 24    where cumulative_qty is null
 25  /

3 rows merged.

SQL> select * from txn order by group_id, txn_id;
    TXN_ID   GROUP_ID TXN_DATE      QUANTITY CUMULATIVE_QTY
---------- ---------- ----------- ---------- --------------
         1          1 12-AUG-2014         10             10
         2          1 13-AUG-2014         -5              5
         3          1 14-AUG-2014          2              7
         6          1 25-AUG-2014          5             12
         7          1 26-AUG-2014          2             14
         4          2 23-AUG-2014         15             15
         5          2 23-AUG-2014         15             30
         8          2 25-AUG-2014        -10             20
         9          3 28-AUG-2014        100

9 rows selected.

The SELECT retrieves only 5 rows (1 per group with not null cumulative_qty + the rows to update).
To do: the groups that have no cumulative_qty in any row.
Re: Oracle sql query to pick current and previous records [message #622897 is a reply to message #622893] Mon, 01 September 2014 08:51 Go to previous messageGo to next message
Michel Cadot
Messages: 68645
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator

Here's a statement that handles this latter case:
SQL> select * from txn order by group_id, txn_id;
    TXN_ID   GROUP_ID TXN_DATE      QUANTITY CUMULATIVE_QTY
---------- ---------- ----------- ---------- --------------
         1          1 12-AUG-2014         10             10
         2          1 13-AUG-2014         -5              5
         3          1 14-AUG-2014          2              7
         6          1 25-AUG-2014          5
         7          1 26-AUG-2014          2
         4          2 23-AUG-2014         15             15
         5          2 23-AUG-2014         15             30
         8          2 25-AUG-2014        -10
         9          3 28-AUG-2014        100

9 rows selected.

SQL> merge
  2    into txn t
  3    using (with
  4             maxtxn as (
  5               select group_id, max(txn_id) maxtxn
  6               from txn
  7               where cumulative_qty is not null
  8               group by group_id
  9               union all
 10               select group_id, min(txn_id)
 11               from txn
 12               group by group_id
 13               having max(cumulative_qty) is null
 14             )
 15           select t.group_id, t.txn_id, t.quantity,
 16                  nvl(last_value (t.cumulative_qty ignore nulls)
 17                        over (partition by t.group_id order by t.txn_id),
 18                      t.quantity)
 19                  + sum(decode(t.txn_id,m.maxtxn,0,t.quantity)) over
 20                      (partition by t.group_id
 21                       order by t.txn_id
 22                       rows between unbounded preceding and current row)
 23                    cumulative_qty
 24           from txn t, maxtxn m
 25           where t.group_id = m.group_id
 26             and t.txn_id >= m.maxtxn) x
 27    on (t.txn_id = x.txn_id)
 28  when matched then
 29    update set cumulative_qty = nvl(x.cumulative_qty, x.quantity)
 30    where cumulative_qty is null
 31  /

4 rows merged.

SQL> select * from txn order by group_id, txn_id;
    TXN_ID   GROUP_ID TXN_DATE      QUANTITY CUMULATIVE_QTY
---------- ---------- ----------- ---------- --------------
         1          1 12-AUG-2014         10             10
         2          1 13-AUG-2014         -5              5
         3          1 14-AUG-2014          2              7
         6          1 25-AUG-2014          5             12
         7          1 26-AUG-2014          2             14
         4          2 23-AUG-2014         15             15
         5          2 23-AUG-2014         15             30
         8          2 25-AUG-2014        -10             20
         9          3 28-AUG-2014        100            100

9 rows selected.

Re: Oracle sql query to pick current and previous records [message #622899 is a reply to message #622893] Mon, 01 September 2014 09:14 Go to previous messageGo to next message
Solomon Yakobson
Messages: 3273
Registered: January 2010
Location: Connecticut, USA
Senior Member
Michel,

Your solution doesn't have a better plan:

SQL> set linesize 132
SQL> explain plan for
  2  MERGE INTO txn t1 USING
  3  (SELECT txn_id,
  4    lag(cumulative_qty, 1) over ( partition BY group_id order by txn_id ) AS prev_cum_qty
  5  FROM txn
  6  ) x ON (t1.txn_id = x.txn_id)
  7  WHEN matched THEN
  8    UPDATE
  9    SET cumulative_qty    = quantity + prev_cum_qty
 10    WHERE cumulative_qty IS NULL
 11  /

Explained.

SQL> select  *
  2    from  table(dbms_xplan.display)
  3  /

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3439448641

-------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------
|   0 | MERGE STATEMENT                  |              |     9 |   900 |     4  (25)| 00:00:01 |
|   1 |  MERGE                           | TXN          |       |       |            |          |
|   2 |   VIEW                           |              |       |       |            |          |
|*  3 |    HASH JOIN                     |              |     9 |   891 |     4  (25)| 00:00:01 |
|   4 |     VIEW                         |              |     9 |   234 |     0   (0)| 00:00:01 |
|   5 |      WINDOW BUFFER               |              |     9 |   351 |     0   (0)| 00:00:01 |

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------
|   6 |       TABLE ACCESS BY INDEX ROWID| TXN          |     9 |   351 |     0   (0)| 00:00:01 |
|   7 |        INDEX FULL SCAN           | IX_GROUP_TXN |     9 |       |     0   (0)| 00:00:01 |
|   8 |     TABLE ACCESS FULL            | TXN          |     9 |   657 |     3   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("T1"."TXN_ID"="X"."TXN_ID")

Note

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------
-----
   - dynamic sampling used for this statement (level=2)

24 rows selected.

SQL> explain plan for
  2  merge
  3    into txn t
  4    using (with
  5             maxtxn as (
  6               select group_id, max(txn_id) maxtxn
  7               from txn
  8               where cumulative_qty is not null
  9               group by group_id
 10             )
 11           select t.group_id, t.txn_id, t.quantity,
 12                  last_value (t.cumulative_qty ignore nulls)
 13                    over (partition by t.group_id order by t.txn_id)
 14                  + sum(decode(t.txn_id,m.maxtxn,0,t.quantity)) over
 15                      (partition by t.group_id
 16                       order by t.txn_id
 17                       rows between unbounded preceding and current row)
 18                    cumulative_qty
 19           from txn t, maxtxn m
 20           where t.group_id = m.group_id
 21             and t.txn_id >= m.maxtxn) x
 22    on (t.txn_id = x.txn_id)
 23  when matched then
 24    update set cumulative_qty = nvl(x.cumulative_qty, x.quantity)
 25    where cumulative_qty is null
 26  /

Explained.

SQL> select  *
  2    from  table(dbms_xplan.display)
  3  /

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 3559473924

------------------------------------------------------------------------------------------------------
| Id  | Operation                             | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | MERGE STATEMENT                       |              |     1 |   100 |     5  (20)| 00:00:01 |
|   1 |  MERGE                                | TXN          |       |       |            |          |
|   2 |   VIEW                                |              |       |       |            |          |
|   3 |    NESTED LOOPS                       |              |       |       |            |          |
|   4 |     NESTED LOOPS                      |              |     1 |   125 |     5  (20)| 00:00:01 |
|   5 |      VIEW                             |              |     1 |    52 |     4  (25)| 00:00:01 |

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------
|   6 |       WINDOW SORT                     |              |     1 |    65 |     4  (25)| 00:00:01 |
|   7 |        NESTED LOOPS                   |              |     1 |    65 |     3   (0)| 00:00:01 |
|   8 |         TABLE ACCESS FULL             | TXN          |     9 |   468 |     3   (0)| 00:00:01 |
|*  9 |         VIEW PUSHED PREDICATE         |              |     1 |    13 |     0   (0)| 00:00:01 |
|* 10 |          FILTER                       |              |       |       |            |          |
|  11 |           SORT AGGREGATE              |              |     1 |    39 |            |          |
|* 12 |            TABLE ACCESS BY INDEX ROWID| TXN          |     1 |    39 |     0   (0)| 00:00:01 |
|* 13 |             INDEX RANGE SCAN          | IX_GROUP_TXN |     1 |       |     0   (0)| 00:00:01 |
|* 14 |      INDEX UNIQUE SCAN                | PK_TXN       |     1 |       |     0   (0)| 00:00:01 |
|  15 |     TABLE ACCESS BY INDEX ROWID       | TXN          |     1 |    73 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   9 - filter("T"."TXN_ID">="M"."MAXTXN")
  10 - filter(COUNT(*)>0)
  12 - filter("CUMULATIVE_QTY" IS NOT NULL)
  13 - access("GROUP_ID"="T"."GROUP_ID")
  14 - access("T"."TXN_ID"="X"."TXN_ID")

Note

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------
-----
   - dynamic sampling used for this statement (level=2)

35 rows selected.

SQL>


Also, OP needs to clarify unprocessed rows. Can unprocessed row transaction date be less than processed row transaction date? If it can, we need to recalculate all cumulative_qty. But main thing is non-scalable design. Storing cumulative values implies serialization. We will get bad results if multiple sessions will try to recalculate cumulative_qty at the same time.
Anyway, I'l assume unprocessed row transaction date is greater than processed row transaction date and process will be serialized. First issue we face here is lack of indexes. I will drop OP provided index and will create:

create index txn_idx1
  on txn(
         nvl2(cumulative_qty,null,1),
         quantity
        )
/
create index txn_idx2
  on txn(
         group_id,
         txn_date,
         cumulative_qty
        )
/


Now:

SQL> select * from txn order by group_id,txn_date;

    TXN_ID   GROUP_ID TXN_DATE    QUANTITY CUMULATIVE_QTY
---------- ---------- --------- ---------- --------------
         1          1 12-AUG-14         10             10
         2          1 13-AUG-14         -5              5
         3          1 14-AUG-14          2              7
         6          1 25-AUG-14          5
         7          1 26-AUG-14          2
         4          2 23-AUG-14         15             15
         5          2 23-AUG-14         15             30
         8          2 25-AUG-14        -10
         9          3 28-AUG-14        100

9 rows selected.

SQL> set autotrace on
SQL> merge
  2    into txn t
  3    using(
  4          select  rowid rid,
  5                  sum(quantity) over(partition by group_id order by txn_date) +
  6                  nvl(
  7                      (
  8                       select  max(b.cumulative_qty) keep(dense_rank last order by txn_date)
  9                         from  txn b
 10                         where b.group_id = a.group_id
 11                           and b.txn_date < a.txn_date
 12                           and b.cumulative_qty is not null
 13                      ),
 14                     0
 15                    ) cumulative_qty
 16            from  txn a
 17            where nvl2(cumulative_qty,null,1) = 1
 18         ) s
 19    on (
 20            t.rowid = s.rid
 21       )
 22    when matched
 23      then update
 24              set cumulative_qty = s.cumulative_qty
 25  /

4 rows merged.


Execution Plan
----------------------------------------------------------
Plan hash value: 2525700079

---------------------------------------------------------------------------------------------
| Id  | Operation                        | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | MERGE STATEMENT                  |          |     1 |    61 |     4  (25)| 00:00:01 |
|   1 |  MERGE                           | TXN      |       |       |            |          |
|   2 |   SORT AGGREGATE                 |          |     1 |    35 |            |          |
|*  3 |    INDEX RANGE SCAN              | TXN_IDX2 |     1 |    35 |     1   (0)| 00:00:01 |
|   4 |   VIEW                           |          |       |       |            |          |
|   5 |    NESTED LOOPS                  |          |     1 |    98 |     4  (25)| 00:00:01 |
|   6 |     VIEW                         |          |     1 |    25 |     3  (34)| 00:00:01 |
|   7 |      WINDOW SORT                 |          |     1 |    50 |     3  (34)| 00:00:01 |
|   8 |       TABLE ACCESS BY INDEX ROWID| TXN      |     1 |    50 |     2   (0)| 00:00:01 |
|*  9 |        INDEX RANGE SCAN          | TXN_IDX1 |     1 |       |     1   (0)| 00:00:01 |
|  10 |     TABLE ACCESS BY USER ROWID   | TXN      |     1 |    73 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("B"."GROUP_ID"=:B1 AND "B"."TXN_DATE"<:B2)
       filter("B"."CUMULATIVE_QTY" IS NOT NULL)
   9 - access(NVL2("CUMULATIVE_QTY",NULL,1)=1)

Note
-----
   - dynamic sampling used for this statement (level=2)


Statistics
----------------------------------------------------------
          0  recursive calls
         38  db block gets
          5  consistent gets
          0  physical reads
          0  redo size
        847  bytes sent via SQL*Net to client
       1503  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          2  sorts (memory)
          0  sorts (disk)
          4  rows processed

SQL> set autotrace off
SQL> select * from txn order by group_id,txn_date;

    TXN_ID   GROUP_ID TXN_DATE    QUANTITY CUMULATIVE_QTY
---------- ---------- --------- ---------- --------------
         1          1 12-AUG-14         10             10
         2          1 13-AUG-14         -5              5
         3          1 14-AUG-14          2              7
         6          1 25-AUG-14          5             12
         7          1 26-AUG-14          2             14
         4          2 23-AUG-14         15             15
         5          2 23-AUG-14         15             30
         8          2 25-AUG-14        -10             20
         9          3 28-AUG-14        100            100

9 rows selected.

SQL>


However, we are executing select list subquery for every row where cumulative_qty is null (for every unprocessed row), so it all depends on how many unprocessed rows there are. Full scan will be faster for large(r) number of unprocessed rows.

SY.
Re: Oracle sql query to pick current and previous records [message #622902 is a reply to message #622899] Mon, 01 September 2014 10:21 Go to previous messageGo to next message
Michel Cadot
Messages: 68645
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator

Quote:
Your solution doesn't have a better plan:


We can't compare as OP's statement is wrong and does not give the expected result. Wink

Quote:
Also, OP needs to clarify unprocessed rows. Can unprocessed row transaction date be less than processed row transaction date? If it can, we need to recalculate all cumulative_qty. But main thing is non-scalable design. Storing cumulative values implies serialization. We will get bad results if multiple sessions will try to recalculate cumulative_qty at the same time.


I agree with you (and it was my first answer which I never posted) but, given that rows contain a value up to a transaction within a group (assumption made from the test case and from "Now I need a merge script that need to fetch only unprocessed new records"), I assumed this column is set by a batch during which there is no other session. Smile

Re: Oracle sql query to pick current and previous records [message #622919 is a reply to message #622902] Mon, 01 September 2014 14:11 Go to previous messageGo to next message
Solomon Yakobson
Messages: 3273
Registered: January 2010
Location: Connecticut, USA
Senior Member
Michel Cadot wrote on Mon, 01 September 2014 11:21

We can't compare as OP's statement is wrong and does not give the expected result. Wink


Oops. OP complained about his solution performance but never mentioned wrong results, so I assumed it works OK. Anyway, OP wrote: "In the above merge query, the select query fetching all the rows (in this case all 9 records) and merge 4 records. This hitting performance issue. I want the select query to return only the unprocessed rows(txn_id: 6,7,8,9) and latest processed rows(txn_id: 3,5)". We have to add last_txn_flag column to implement the above:

SQL> select  *
  2    from  txn
  3    order by group_id,
  4             txn_date
  5  /

    TXN_ID   GROUP_ID TXN_DATE    QUANTITY CUMULATIVE_QTY LAST_TXN_FLAG
---------- ---------- --------- ---------- -------------- -------------
         1          1 12-AUG-14         10             10             0
         2          1 13-AUG-14         -5              5             0
         3          1 14-AUG-14          2              7             1
         6          1 25-AUG-14          5
         7          1 26-AUG-14          2
         4          2 23-AUG-14         15             15             0
         5          2 23-AUG-14         15             30             1
         8          2 25-AUG-14        -10
         9          3 28-AUG-14        100

9 rows selected.


We also need FBI:

create index txn_idx1
  on txn(
         nvl2(cumulative_qty,last_txn_flag,1),
         group_id,
         txn_date,
         cumulative_qty,
         quantity
        )
/


Now:

SQL> set autotrace on explain
SQL> merge
  2    into txn t
  3    using(
  4          select  rowid rid,
  5                  sum(
  6                      nvl2(cumulative_qty,0,quantity) + nvl(cumulative_qty,0)
  7                     ) over(partition by group_id order by txn_date) cumulative_qty,
  8                  lead(0,1,1) over(partition by group_id order by txn_date) last_txn_flag
  9            from  txn a
 10            where nvl2(cumulative_qty,last_txn_flag,1) = 1
 11         ) s
 12    on (
 13            t.rowid = s.rid
 14       )
 15    when matched
 16      then update
 17              set cumulative_qty = s.cumulative_qty,
 18                  last_txn_flag  = s.last_txn_flag
 19  /

6 rows merged.


Execution Plan
----------------------------------------------------------
Plan hash value: 3419488159

------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | MERGE STATEMENT               |          |     1 |    77 |     3  (34)| 00:00:01 |
|   1 |  MERGE                        | TXN      |       |       |            |          |
|   2 |   VIEW                        |          |       |       |            |          |
|   3 |    NESTED LOOPS               |          |     1 |   114 |     3  (34)| 00:00:01 |
|   4 |     VIEW                      |          |     1 |    28 |     2  (50)| 00:00:01 |
|   5 |      WINDOW BUFFER            |          |     1 |    73 |     2  (50)| 00:00:01 |
|*  6 |       INDEX RANGE SCAN        | TXN_IDX1 |     1 |    73 |     1   (0)| 00:00:01 |
|   7 |     TABLE ACCESS BY USER ROWID| TXN      |     1 |    86 |     1   (0)| 00:00:01 |
------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   6 - access(NVL2("CUMULATIVE_QTY","LAST_TXN_FLAG",1)=1)

Note
-----
   - dynamic sampling used for this statement (level=2)

SQL> set autotrace off
SQL> select  *
  2    from  txn
  3    order by group_id,
  4             txn_date
  5  /

    TXN_ID   GROUP_ID TXN_DATE    QUANTITY CUMULATIVE_QTY LAST_TXN_FLAG
---------- ---------- --------- ---------- -------------- -------------
         1          1 12-AUG-14         10             10             0
         2          1 13-AUG-14         -5              5             0
         3          1 14-AUG-14          2              7             0
         6          1 25-AUG-14          5             12             0
         7          1 26-AUG-14          2             14             1
         4          2 23-AUG-14         15             15             0
         5          2 23-AUG-14         15             30             0
         8          2 25-AUG-14        -10             20             1
         9          3 28-AUG-14        100            100             1

9 rows selected.

SQL>


SY.
Re: Oracle sql query to pick current and previous records [message #622974 is a reply to message #622880] Tue, 02 September 2014 15:13 Go to previous message
Michel Cadot
Messages: 68645
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator

Any feedback on the work that have been done and help provided?

Previous Topic: Issue getting all data using WITH clause
Next Topic: Find missing sequence number between given range
Goto Forum:
  


Current Time: Wed Apr 24 23:14:29 CDT 2024