Dynamic Query for PIVOT In Clause

依然范特西╮ 提交于 2019-12-12 04:51:47

问题


This is in continuation of this thread I have the below query to have the value as heading using

  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

Can I use the below query to use inside the IN clause of PIVOT to have the dates dynamic? The idea is to have a dynamic query inside the IN clause to avoid hard-coding of dates

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))

Bind values

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

The output of the above is

19-Sep-17
18-Sep-17

Expected output is

╔═════════╦════════════╦════════╦════════╦═══════════╗
║ 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         ║
╚═════════╩════════════╩════════╩════════╩═══════════╝

Table structure and values

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;

回答1:


Something like:

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;



回答2:


First of all, it's quite ridiculous to use pivot by dates when original date column also part of expected output. This introduces new columns but row count remains the same.

Secondly, it's not possible to make column names depend on bind values. Column names are defined on parse stage and Oracle re-uses the same plan for different binds.

If you, however, want to pass start and end_date as binds and have predefined column titles you can use either

  • case (or decode) + group by
  • pivot for xml

Update:

Normal solution

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.

Bizarre solution

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.

As for DSQL approach for this task... this is far away from common sense.



来源:https://stackoverflow.com/questions/46441419/dynamic-query-for-pivot-in-clause

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!