简体   繁体   中英

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM