【问题标题】:tricky Oracle analytic function question棘手的Oracle解析函数问题
【发布时间】:2010-02-18 18:44:33
【问题描述】:

我是 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. 7 天最大值的对应日期 (value_date)
4.最近30天数据的“my_value”最大值
5. 30天最大值的对应日期(value_date)

是否可以在没有连接的情况下执行此操作?实现这一目标的最佳方法是什么?

提前感谢您的帮助,

卡尔

【问题讨论】:

    标签: oracle analytic-functions


    【解决方案1】:

    我无法在这里找到我想要的答案,所以我在 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】:

      我认为这对您来说是正确的解决方案。我只是在浏览器中输入它并没有测试。我认为这应该对您有所帮助...至少尝试给您一个想法...

      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
      

      【讨论】:

      • 谢谢赞戈。我感谢您的努力,但我正在尝试使用分析函数来避免嵌套查询和多次访问同一个表。
      【解决方案3】:

      您可以在同一时间间隔内一次性获取信息。 IE。最近 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)
      ;
      

      这里我使用了左外连接,因为可能一周内没有数据,但一个月内有一些数据。

      【讨论】:

        【解决方案4】:
        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
        

        【讨论】:

        • 这里我使用了Rownum Analytical函数来实现。
        【解决方案5】:

        此解决方案仅涉及 2 个表格通行证,并且不使用硬编码日期。

        我使用了 2 个子查询,使用 row_number() 基本上根据 value_date 进行排序,以分别获取 7 天范围和 30 天范围内的最新日期。使用该数据集根据最大 my_value 对记录进行排名,选择排名 = 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
        

        【讨论】:

        • 感谢 mudyfish 和 mogsdad 帮助修复格式...因为这是我的第一个答案,所以无法弄清楚如何使用提供的提示正确格式化它
        • 通过将年份和月份更新为 2015 年 7 月/8 月,稍微更改了示例数据,以检查使用上述基于 sysdate 的解决方案我会得到什么结果......但后来想出了一个通用的方法日期前后。
        【解决方案6】:
        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;
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-03-07
          • 1970-01-01
          • 1970-01-01
          • 2011-10-26
          • 1970-01-01
          • 2011-07-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多