【问题标题】:Dynamic Query for PIVOT In ClausePIVOT In 子句的动态查询
【发布时间】:2018-03-08 13:27:25
【问题描述】:

这是thread的延续 我有以下查询以使用

将值作为标题
  SELECT *
    FROM (SELECT prod_id,
                 start_date AS dt,
                 start_date,
                 hours
            FROM prod_timings t) PIVOT (SUM (hours)
                                 FOR start_date
                                 IN  (TO_DATE ('18-SEP-17', 'DD-MON-YY') AS wed,
                                     TO_DATE ('19-SEP-17', 'DD-MON-YY') AS thu))
ORDER BY prod_id, dt

我可以使用以下查询在 PIVOT 的 IN 子句中使用动态日期吗?这个想法是在 IN 子句中进行动态查询,以避免对日期进行硬编码

SELECT *
  FROM (    SELECT (TO_DATE (:end_date, 'DD-MM-YYYY') - LEVEL + 1) AS day
              FROM DUAL
        CONNECT BY LEVEL <=
                      (  TO_DATE (:end_date, 'DD-MM-YYYY')
                       - TO_DATE (:start_date, 'DD-MM-YYYY')
                       + 1))

绑定值

end_date - 19-Sep-17
start_date - 18-Sep-17

上面的输出是

19-Sep-17
18-Sep-17

预期输出是

╔═════════╦════════════╦════════╦════════╦═══════════╗
║ PROD_ID ║ START_DATE ║ MON-18 ║ TUE-19 ║ TOT_HOURS ║
╠═════════╬════════════╬════════╬════════╬═══════════╣
║ PR220   ║ 19-Sep-17  ║        ║ 0      ║ 0         ║
║ PR2230  ║ 19-Sep-17  ║        ║ 2      ║ 2         ║
║ PR9702  ║ 19-Sep-17  ║        ║ 3      ║ 3         ║
║ PR9036  ║ 19-Sep-17  ║        ║ 0.6    ║ 0.6       ║
║ PR9036  ║ 18-Sep-17  ║ 3.4    ║        ║ 3.4       ║
║ PR9609  ║ 18-Sep-17  ║ 5      ║        ║ 5         ║
║ PR91034 ║ 18-Sep-17  ║ 4      ║        ║ 4         ║
║ PR7127  ║ 18-Sep-17  ║ 0      ║        ║ 0         ║
╚═════════╩════════════╩════════╩════════╩═══════════╝

表结构和值

CREATE TABLE PROD_TIMINGS
(
  PROD_ID     VARCHAR2(12 BYTE),
  START_DATE  DATE,
  TOT_HOURS   NUMBER
);

SET DEFINE OFF;
Insert into PROD_TIMINGS
   (PROD_ID, START_DATE, TOT_HOURS)
 Values
   ('PR220', TO_DATE('09/19/2017 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 0);
Insert into PROD_TIMINGS
   (PROD_ID, START_DATE, TOT_HOURS)
 Values
   ('PR2230', TO_DATE('09/19/2017 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 2);
Insert into PROD_TIMINGS
   (PROD_ID, START_DATE, TOT_HOURS)
 Values
   ('PR9702', TO_DATE('09/19/2017 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 3);
Insert into PROD_TIMINGS
   (PROD_ID, START_DATE, TOT_HOURS)
 Values
   ('PR9036', TO_DATE('09/19/2017 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 0.6);
Insert into PROD_TIMINGS
   (PROD_ID, START_DATE, TOT_HOURS)
 Values
   ('PR9036', TO_DATE('09/18/2017 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 3.4);
Insert into PROD_TIMINGS
   (PROD_ID, START_DATE, TOT_HOURS)
 Values
   ('PR9609', TO_DATE('09/18/2017 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 5);
Insert into PROD_TIMINGS
   (PROD_ID, START_DATE, TOT_HOURS)
 Values
   ('PR91034', TO_DATE('09/18/2017 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 4);
Insert into PROD_TIMINGS
   (PROD_ID, START_DATE, TOT_HOURS)
 Values
   ('PR7127', TO_DATE('09/18/2017 00:00:00', 'MM/DD/YYYY HH24:MI:SS'), 0);
COMMIT;

【问题讨论】:

    标签: sql oracle oracle11g pivot


    【解决方案1】:

    首先,当原始日期列也是预期输出的一部分时,使用按日期透视是非常荒谬的。这会引入新列,但行数保持不变。

    其次,不可能使列名依赖于绑定值。列名在解析阶段定义,Oracle 为不同的绑定重复使用相同的计划。

    但是,如果您想将 start 和 end_date 作为绑定传递并具有预定义的列标题,您可以使用其中任何一个

    • case(或解码)+ group by
    • xml 的枢轴

    更新:

    正常解

    SQL> select prod_id, dt,
      2         sum(decode(dt, :start_date, tot_hours)) start_date_hours,
      3         sum(decode(dt, :end_date, tot_hours)) end_date_hours,
      4         sum(tot_hours) tot_hours
      5    from (select prod_id, start_date as dt, start_date, tot_hours
      6            from prod_timings t)
      7   group by dt, prod_id
      8   order by dt desc, prod_id;
    
    PROD_ID      DT            START_DATE_HOURS       END_DATE_HOURS  TOT_HOURS
    ------------ --------- -------------------- -------------------- ----------
    PR220        19-SEP-17                                        .0          0
    PR2230       19-SEP-17                                       2.0          2
    PR9036       19-SEP-17                                        .6         .6
    PR9702       19-SEP-17                                       3.0          3
    PR7127       18-SEP-17                   .0                               0
    PR9036       18-SEP-17                  3.4                             3.4
    PR91034      18-SEP-17                  4.0                               4
    PR9609       18-SEP-17                  5.0                               5
    
    8 rows selected.
    

    奇怪的解决方案

    SQL> with t as
      2  (select *
      3    from (select prod_id, start_date as dt, start_date, tot_hours
      4            from prod_timings t)
      5  pivot xml(sum(tot_hours) as s for start_date in
      6  (select :start_date from dual union all select :end_date from dual)))
      7  select prod_id, dt, start_date_hours, end_date_hours,
      8         nvl(start_date_hours, end_date_hours) tot_hours
      9  from t,
     10   xmltable('/PivotSet' passing start_date_xml
     11            columns
     12            start_date_hours number
     13            path '/PivotSet/item[1]/column[@name="S"]/text()',
     14            end_date_hours number
     15            path '/PivotSet/item[2]/column[@name="S"]/text()') x
     16   order by dt desc, prod_id;
    
    PROD_ID      DT            START_DATE_HOURS       END_DATE_HOURS  TOT_HOURS
    ------------ --------- -------------------- -------------------- ----------
    PR220        19-SEP-17                                        .0          0
    PR2230       19-SEP-17                                       2.0          2
    PR9036       19-SEP-17                                        .6         .6
    PR9702       19-SEP-17                                       3.0          3
    PR7127       18-SEP-17                   .0                               0
    PR9036       18-SEP-17                  3.4                             3.4
    PR91034      18-SEP-17                  4.0                               4
    PR9609       18-SEP-17                  5.0                               5
    
    8 rows selected.
    

    至于这项任务的 DSQL 方法……这与常识相去甚远。

    【讨论】:

    • 这个想法是避免在第一条 SQL 语句中对日期进行硬编码。如果你能举个例子,不胜感激。
    • @user75ponic,感谢减号。 :) 我为您提供了两种方法来实现您想要的。如果你想使用 pivot 子句,那么使用 pivot xml + subquery 但在这种情况下 group by 会简单得多。如果您不想构造和解析 XML,则 pivot 将失败并出现 ORA-56900: bind variable is not supported inside pivot|unpivot operation
    • pivot xml ( sum(tot_hours) for start_date IN (select :start_date from dual union all select :end_date from dual)
    • 我没有做减号或负号标记,我当然不会这样做。你可以看到我的个人资料评论,我提到我永远不会投反对票。
    • 也许您可以联系管理员以跟踪日志以查找负面或否决历史记录。
    【解决方案2】:

    类似:

    VARIABLE cur REFCURSOR;
    
    DECLARE
      dates VARCHAR2(4000);
      start_date DATE := DATE '2017-09-18';
      end_date   DATE := DATE '2017-09-19';
    BEGIN
      SELECT LISTAGG(
               'DATE ''' || TO_CHAR( dt, 'YYYY-MM-DD' )
               || ''' AS "' || TO_CHAR( dt, 'FMDY-DD' ) || '"',
               ','
             ) WITHIN GROUP ( ORDER BY dt )
      INTO   dates
      FROM   (
        SELECT start_date + LEVEL - 1 AS dt
        FROM   DUAL
        CONNECT BY LEVEL <= end_date - start_date + 1
      );
    
      OPEN :cur FOR
      'SELECT * FROM (
         SELECT t.*,
                MIN( start_date ) OVER ( PARTITION BY prod_id ) AS min_start_date,
                SUM( tot_hours  ) OVER ( PARTITION BY prod_id ) AS prod_tot_hours
         FROM   prod_timings t
         WHERE  start_date BETWEEN :1 AND :2
       )
       PIVOT (
         SUM( tot_hours )
         FOR start_date IN (' || dates || ')
       )
       ORDER BY prod_id'
       USING start_date, end_date;
    END;
    /
    
    PRINT cur;
    

    【讨论】:

    • 现在解释一下如果没有 DSQL 也能实现结果,那么使用 DSQL 的意义何在。
    • 太棒了,我发自内心地感激,这太棒了。如果这可以通过 SQL 实现,那就太好了。
    • 大概,我可以使用你的sn-p代码的函数,并通过传递必要的参数从SQL调用函数。
    • 是的...但是从 SQL 中调用的函数将在单个结果集行中返回 cursor expression - 它不会生成多行。
    • @MT0 我没有注意到,有什么解决方法吗?
    猜你喜欢
    • 2013-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-24
    • 1970-01-01
    • 2015-04-02
    • 2010-10-23
    • 1970-01-01
    相关资源
    最近更新 更多