Noah Maverick Noah Maverick - 19 days ago 6
SQL Question

oracle sql query optimization further 1

I have written a query to select * from bdb to get only updated values in PRICE for the combination of DAY,INST in the newest ACT
I created A table like

CREATE TABLE bdb(
ACT NUMBER(8) NOT NULL,
INST NUMBER(8) NOT NULL,
DAY DATE NOT NULL,
PRICE VARCHAR2 (3),
CURR NUMBER (8,2),
PRIMARY KEY (ACT,INST,DAY)
);


used this to populate the table

DECLARE
t_day bdb.day%type:= '1-JAN-16';
n pls_integer;
BEGIN


<< act_loop >>
FOR i IN 1..3 LOOP --NUMBER OF ACT i
<< inst_loop >>
FOR j IN 1..1000 LOOP --NUMBER OF INST j
t_day:='3-JAN-16';
<< day_loop >>
FOR k IN 1..260 LOOP --NUMBER OF DAYS k
n:= dbms_random.value(1,3);
INSERT into bdb (ACT,INST,DAY,PRICE,CURR) values (i,j,t_day,n,10.3);
t_day:=t_day+1;
END loop day_loop;

END loop inst_loop;
END loop act_loop;

END;
/


using this query
I get only the DAY,INST,PRICE

select day,inst,price from bdb where (act=(select max(act) from bdb))
minus
select day,inst,price from bdb where act=(select max(act)-1 from bdb);


above one is fast.but I want to get all the field in efficient way.
the one I came up with bit slow which is this,

select
e1.*
from
(select
*
from
bdb
where
(act=(select max(act) from bdb))
)e1,
(select day,inst,price from bdb where (act=(select max(act) from bdb))
minus
select day,inst,price from bdb where act=(select max(act)-1 from bdb)) e2
where
e1.day=e2.day and e1.inst=e2.inst;


can anyone give any suggestion to how to optimized this any more? or with out using cross join with two table how to get the required output.Help me ;)

simply I need is

ACT INST DAY PRI CURR
------------------------------------
3 890 05-MAR-16 3 10.3
3 890 06-MAR-16 2 10.3
3 890 07-MAR-16 2 10.3

3 891 05-MAR-16 2 10.3
3 891 06-MAR-16 1 10.3
3 891 07-MAR-16 2 10.3

4 890 05-MAR-16 3 10.3
4 890 06-MAR-16 2 10.3
4 890 07-MAR-16 1 10.3

4 891 05-MAR-16 2 10.3
4 891 06-MAR-16 2 10.3
4 891 07-MAR-16 1 10.3


Here for (890,05-MAR-16) (890,06-MAR-16) (890,06-MAR-16)
(891,05-MAR-16) (891,06-MAR-16) (891,06-MAR-16) in act=3
price are
3,2,2
2,1,2

but when act=4 happens
(890,07-MAR-16)
(891,06-MAR-16)
(891,07-MAR-16)
price values are change from what they were in act=3.
others not change


ultimately what I need is

ACT INST DAY PRI CURR
------------------------------------
4 890 07-MAR-16 1 10.3
4 891 06-MAR-16 2 10.3
4 891 07-MAR-16 1 10.3

Answer

It looks like you're after the day, inst and price values which have a row where the act column has the maximum act value out of the whole table, but doesn't have a row where the act column is one less than the max act value.

You could try this:

SELECT day,
       inst,
       price
FROM   (SELECT day,
               inst,
               price,
               act,
               MAX(act) OVER () max_overall_act
        FROM   bdb)
WHERE  act IN (max_overall_act, max_overall_act -1)
GROUP BY day, inst, price
HAVING MAX(CASE WHEN act = max_overall_act THEN 1 END) = 1
AND    MAX(CASE WHEN act = max_overall_act - 1 THEN 1 END) IS NULL;

First of all, the subquery finds the maximum act value across the whole table.

Then we select all rows that have an act value that is the maximum value or one less than that.

After that, we group the rows and find out which ones have an act = max act val, but don't have an act = max act val -1.


However, from what you said in your post:

I have written a query to select * from bdb to get only updated values in PRICE for the combination of DAY,INST in the newest ACT

neither the query you came up with and the above query in my answer seem to tally with what you are after.

I think instead, you're after something like:

SELECT act,
       inst,
       DAY,
       price,
       curr,
       prev_price -- if desired
FROM   (SELECT act,
               inst,
               DAY,
               price,
               curr,
               LEAD(price) OVER (PARTITION BY inst, DAY ORDER BY act DESC) prev_price,
               row_number() OVER (PARTITION BY inst, DAY ORDER BY act DESC) rn
        FROM   bdb)
WHERE  rn = 1
AND    prev_price != price;

What this does is use the LEAD() analytic (based on the descending act order) to find the price of the row with the previous act for each inst and day, along with the rownumber.

Then to find the latest act row, we simply select the rows where the rownumber is 1 and also where the previous price doesn't match the current price. You can then display both the current and the previous price, if you want to.

Comments