хитрый вопрос аналитической функции Oracle - PullRequest
2 голосов
/ 18 февраля 2010

Я новичок в аналитических функциях Oracle и пытаюсь найти лучший способ написания моего запроса.

Ниже приведена упрощенная версия таблицы, с которой я работаю ...


CREATE TABLE my_table
(
    pid          NUMBER  NOT NULL,
    my_value     NUMBER,
    value_date   DATE    NOT NULL,

    CONSTRAINT pk_my_table PRIMARY KEY (pid, value_date)
);

Обратите внимание, что "value_date" является частью первичного ключа.

Для каждого "pid" я бы хотел, чтобы запрос отображал 5 столбцов:
1. pid
2. максимальное значение «my_value» для последних 7 дней данных
3. соответствующий день (value_date) для 7-дневного максимума
4. максимальное значение «my_value» для большинстваданные за последние 30 дней
5. соответствующий день (значение_даты) для 30-дневного максимума

Возможно ли это сделать без объединения?Каков наилучший способ сделать это?

Заранее спасибо за любую помощь,

Кал

Ответы [ 6 ]

3 голосов
/ 18 февраля 2010

Я не смог получить ответ, который искал здесь, поэтому я разместил вопрос на форумах Oracle. Ответ, который я получил, был именно тем, что я искал. Следующий запрос получает ответ только с одним проходом через таблицу и без каких-либо объединений ...


  SELECT   pid,
           MAX(CASE
                  WHEN value_date > TRUNC (SYSDATE) - 6 THEN my_value
                  ELSE 0
               END)
              max_7_days,
           MAX(value_date)
              KEEP (DENSE_RANK FIRST ORDER BY
                                        (CASE
                                            WHEN value_date >
                                                    TRUNC (SYSDATE) - 6
                                            THEN
                                               my_value
                                            ELSE
                                               0
                                         END) DESC)
              day_7_days,
           MAX(CASE
                  WHEN value_date > TRUNC (SYSDATE) - 29 THEN my_value
                  ELSE 0
               END)
              max_30_days,
           MAX(value_date)
              KEEP (DENSE_RANK FIRST ORDER BY
                                        CASE
                                           WHEN value_date >
                                                   TRUNC (SYSDATE) - 29
                                           THEN
                                              my_value
                                           ELSE
                                              0
                                        END DESC)
              day_30_days
    FROM   my_table
GROUP BY   pid;

Если кто-то захочет это проверить, вот несколько примеров данных.


INSERT INTO my_table (pid, my_value, value_date) VALUES (1,    300, '18-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,    200, '17-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,   4500, '16-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,    800, '15-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  12000, '14-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  21000, '13-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,   5600, '12-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  18400, '11-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,   7200, '10-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  24000, '09-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,   8800, '08-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,   6000, '07-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  44200, '06-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,   2800, '05-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  58500, '04-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,   8000, '03-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  30600, '02-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,   7200, '01-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  62700, '31-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  48000, '30-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  16800, '29-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  83600, '28-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  18400, '27-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  16800, '26-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  40000, '25-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  23400, '24-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  99900, '23-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  67200, '22-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,   5800, '21-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  57000, '20-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  96100, '19-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  64000, '18-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  52800, '17-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  71400, '16-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  14000, '15-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,   7200, '14-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1, 111000, '13-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  64600, '12-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1, 113100, '11-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (1,  84000, '10-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,   6000, '18-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,   7800, '17-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,  13500, '16-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,  25600, '15-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 276000, '14-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 210000, '13-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 162400, '12-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,  36800, '11-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,  86400, '10-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 480000, '09-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,   8800, '08-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 102000, '07-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,1237600, '06-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,  61600, '05-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,1287000, '04-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 144000, '03-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 275400, '02-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,  93600, '01-FEB-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,1630200, '31-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,1248000, '30-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 235200, '29-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,3176800, '28-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,  73600, '27-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 403200, '26-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 200000, '25-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 280800, '24-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 599400, '23-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,1612800, '22-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 110200, '21-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,1653000, '20-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,2498600, '19-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,2368000, '18-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 211200, '17-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,2570400, '16-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 224000, '15-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 223200, '14-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2, 999000, '13-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,2325600, '12-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,1017900, '11-JAN-10');
INSERT INTO my_table (pid, my_value, value_date) VALUES (2,2352000, '10-JAN-10');

Спасибо всем за ваши попытки ответить на этот вопрос!

Кал

2 голосов
/ 18 февраля 2010

Я думаю, что это правильное решение для вас. Я только что набрал его в браузере и не проверял. Я думаю, что это должно помочь вам ... По крайней мере, попытался дать вам идею ...

SELECT table_7_day_info.pid
       ,table_7_day_info.max_val_7_day
       ,table_7_day_info.max_day_7_day
       ,table_30_day_info.max_val_30_day
       ,table_30_day_info.max_day_30_day
FROM
    (
       SELECT t1.pid, t1.my_value max_val_7_day, t1.value_date max_day_7_day
       FROM my_table t1
       WHERE (t1.pid,t.my_value) in (
                                     SELECT t.pid, max(t.my_value)
                                     FROM my_table t
                                     WHERE t.value_date >= sysdate - 7
                                     GROUP BY t.pid
                                   )
     ) table_7_day_info,
    (
       SELECT t2.pid, t2.my_value max_val_30_day, t2.value_date max_day_30_day
       FROM my_table t2
       WHERE (t2.pid,t2.my_value) in (
                                     SELECT t3.pid, max(t3.my_value)
                                     FROM my_table t3
                                     WHERE t3.value_date >= sysdate - 30
                                     GROUP BY t3.pid
                                   )
     ) table_30_day_info,
WHERE table_7_day_info.pid = table_7_day_info.pid
1 голос
/ 18 февраля 2010

Вы можете получить информацию за один проход за тот же промежуток времени. То есть информация за последние 7 дней и информация за последние 30 дней. Но чтобы собрать эти данные, вам понадобится объединение:

select nvl(week.pid,mon.pid)
     , week.week_val
     , week.week_date
     , mon.month_val
     , mon.month_date
  from (
         select pid
              , max(my_value) month_val
              , max(value_date) keep (dense_rank last order by my_value) month_date
           from my_table
          where value_date >= sysdate - 30
          group by pid
       ) mon
       left outer join
       (
         select pid
              , max(my_value) week_val
              , max(value_date) keep (dense_rank last order by my_value) week_date
           from my_table
          where value_date >= sysdate - 7
          group by pid
       ) week
       on (mon.pid = week.pid)
;

Здесь я использовал левое внешнее объединение, поскольку возможно, что у вас нет данных в течение недели, но есть данные в течение месяца.

0 голосов
/ 28 августа 2015

Это решение включает в себя только 2 прохода таблицы и не использует жестко закодированные даты.

Я использовал 2 подзапроса, используя row_number () для основной сортировки на основе value_date, чтобы получить самые последние даты в 7-дневном и 30-дневном диапазонах соответственно. Используя этот набор данных, ранжировали записи на основе максимального значения my_value, выбирали верхнюю запись, имеющую rank = 1, и получали максимальное значение, соответствующее и дату.

SELECT daytab7.pid, maxval_7day,maxvaldate_7day,maxval_30day,maxvaldate_30day  
FROM   
  (   /*picking top-most ranked value and corresp. date*/
  SELECT PID, MY_VALUE MAXVAL_7DAY, VALUE_DATE MAXVALDATE_7DAY
  FROM     
    (    /*Assigning rank for range filtered values*/
    SELECT PID, MY_VALUE, VALUE_DATE, RANK() OVER(PARTITION BY PID ORDER BY MY_VALUE DESC) RNK   
    FROM   
       (   /*assigning row_number by ordering rows by value_date desc to get recent data for specified range of 7 days*/
        SELECT PID, MY_VALUE, VALUE_DATE,
        ROW_NUMBER() OVER(PARTITION BY PID ORDER BY VALUE_DATE DESC) ROW_NUM
        FROM   MY_TABLE   
        ORDER  BY 1, 3 DESC   
       )   
    WHERE  ROW_NUM <= 7   
    )   
  WHERE  RNK = 1  
  ) daytab7,  
  (   /*picking top-most ranked value and corresp. date*/
  SELECT PID, MY_VALUE MAXVAL_30DAY, VALUE_DATE MAXVALDATE_30DAY
  FROM     
    (   /*Assigning rank for range filtered values*/ 
    SELECT PID, MY_VALUE, VALUE_DATE, RANK() OVER(PARTITION BY PID ORDER BY MY_VALUE DESC) RNK   
    FROM   
       (   /*assigning row_number by ordering rows by value_date desc to get recent data for specified range of 30 days*/
        SELECT PID, MY_VALUE, VALUE_DATE, ROW_NUMBER() OVER(PARTITION BY PID ORDER BY VALUE_DATE DESC) ROW_NUM
        FROM   MY_TABLE   
        ORDER  BY 1, 3 DESC   
       )   
    WHERE  ROW_NUM <= 30   
    )   
  WHERE  RNK = 1  
  ) daytab30  
WHERE daytab7.pid = daytab30.pid

Вот вывод:

PID | MAXVAL_7DAY | MAXVALDATE_7DAY | MAXVAL_30DAY | MAXVALDATE_30DAY   
1   | 21000 | 8/13/2015 | 99900 | 7/23/2015    
2   | 276000 | 8/14/2015 | 3176800 | 7/28/2015
0 голосов
/ 16 февраля 2014
select t.*,
  max(my_value) over(partition by pid order by value_date range between 0 preceding and 7 following) recent7value,
  max(value_date) over(partition by pid order by value_date range between 0 preceding and 7 following) recent7date,
  max(my_value) over(partition by pid order by value_date range between 0 preceding and 30 following) recent30value,
  max(value_date) over(partition by pid order by value_date range between 0 preceding and 30 following) recent30date
from my_table t
order by pid,
  value_date;
0 голосов
/ 06 ноября 2013
SELECT A.PID,A.VALUE_DATE_7DYMAX, A.MY_VALUE, B.VALUE_DATE_30DYMAX,  B.MY_VALUE FROM (SELECT * FROM (select pid,my_value, VALUE_DATE AS VALUE_DATE_7DYMAX, row_number() over(partition by pid order by MY_VALUE desc) rn FROM MY_TABLE
where value_date between TO_DATE('11-JAN-10','DD-MON-YY') AND TO_DATE('21-JAN-10','DD-MON-YY'))where RN =1) A ,
(SELECT * FROM (select pid,my_value, VALUE_DATE AS VALUE_DATE_30DYMAX, row_number() over(partition by pid order by MY_VALUE desc) rn FROM MY_TABLE
where value_date between TO_DATE('11-JAN-10','DD-MON-YY') AND TO_DATE('21-FEB-10','DD-MON-YY'))where RN =1) B
WHERE A. PID =B.PID
...