条款中PIVOT的动态查询

时间:2017-09-27 07:00:51

标签: sql oracle oracle11g pivot

这是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;

2 个答案:

答案 0 :(得分:1)

类似的东西:

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;

答案 1 :(得分:0)

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

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

但是,如果您希望将start和end_date作为绑定传递并具有预定义的列标题,则可以使用

  • 案例(或解码)+分组
  • pivot for 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方法......这远远不是常识。