简体   繁体   中英

Oracle sql subselect to find aggregated values

I have a table that has product, date/time, and price. I split out the date/time to another column where I just split out the hour part of it. I need to find high/low/open/close of the price value by hour. I can get the high/low easy enough by doing a subselect in the select portion where I max()/min() and join the data by product & hr. I now need the open/close which will be the first record for each hour and the last record for each hour. high/low/open/close should all be the same for each record by hour.

Example result. notice how the high for all hour 7 records is 55 so that's what placed in all the hour 7 records, the low is 30 because it's the lowest of all hour 7 records, 50 is the open because the first price (sorted by date/time at 7:15) is 50. Close is the last price in the hour sorted by date/time which is 30.

Product, Date,            Hour, Price, High, Low, Open, Close
A,       11/12/2012 7:15, 7,    50,    55,   30,  50,   30
A,       11/12/2012 7:28, 7,    55,    55,   30,  50,   30
A,       11/12/2012 7:30, 7,    40,    55,   30,  50,   30
A,       11/12/2012 7:35, 7,    45,    55,   30,  50,   30
A,       11/12/2012 7:55, 7,    30,    55,   30,  50,   30

So again to recap, high/low is easy as I do subselects in the select part that query against the same table doing max/min, but not sure how to do the same thing for open/close to get the first and last records based on date/time field.

Add this as a subquery to get the Open, replacing the product key and hour appropriately:

SELECT * FROM (
  Select Open
  FROM <table name>
  WHERE product = '<product key>' 
    AND hour='<the hour>'
  ORDER BY Date
) WHERE rownum = 1

Add this as a subquery to get the Close, replacing the product key and hour appropriately:

SELECT * FROM (
  Select Close
  FROM <table name>
  WHERE product = '<product key>' 
    AND hour='<the hour>' 
  ORDER BY Date desc
) WHERE rownum = 1

The trick here is setting ordering the result correctly and taking only the first result using rownum = 1 .


Another option:

SELECT Open
FROM <table name>
WHERE product = '<product key>' 
  AND hour='<the hour>' 
  AND Date = (
    SELECT min(Date) 
    FROM <table name> 
    WHERE product = '<product key>' 
    AND hour='<the hour>'
    ) 

SELECT Close
FROM <table name>
WHERE product = '<product key>' 
  AND hour='<the hour>' 
  AND Date = (
    SELECT max(Date) 
    FROM <table name> 
    WHERE product = '<product key>' 
    AND hour='<the hour>'
    ) 

A drawback of this route is that these statements are not guaranteed to return a single row. Meaning if for a given product there two entries for the same hour, with the same Date value that are the min/max it will return 2 rows causing an exception when used as a sub-query.

Although that may be a good thing, instead of it arbitrarily picking a row, you will know there is a particular problem and can possibly update the query to make a more intelligent decision.

I would use ranking functions for this:

select product, datestr, hour,
       max(case when seqnum_open = 1 then price end) as Open,
       max(case when seqnum_close = 1 then price end) as Close,
       max(price) as High,
       min(price) as Low
from (select t.*,
             row_number() over (partition by product, datestr, hour order by date) as seqnum_open,
             row_number() over (partition by product, datestr, hour order by date desc) as seqnum_close
      from (select t.*,
                   to_char(date, 'YYYY-MM-DD') as datestr
            from t
           ) t
     ) t
group by product, datestr, hour

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