2015-04-20 12 views
1

Мои выбрать запрос выглядит следующим образом:Транспонирование строк в столбцы в Oracle 10g: именование столбцов

SELECT 
    R.ID_REQUEST, R.ID_PROJET, J.NAME, L.DATE_ACTION,L.NAME_ACTION 
    , LEAD(L.DATE_ACTION) OVER (PARTITION BY R.ID_REQUEST ORDER BY L.DATE_ACTION)- L.DATE_ACTION AS TOTAL_DAYS 
     FROM (REQUEST R LEFT JOIN LOG_TABLE L ON R.ID_REQUEST = L.ID_REQUEST) 
     LEFT JOIN JOB_TABLE J ON R.ID_JOB = J.ID_JOB 
     WHERE D.ID_REQUEST = 10 

В результате получается нечто вроде этого:

ID_REQUEST ID_PROJET NAME DATE_ACTION NAME_ACTION  TOTAL_DAYS 
10   152  pr1 01/01/2005 arbitrary_name1 3 
10   152  pr1 04/01/2005 arbitrary_name2 1 
10   152  pr1 05/01/2005 arbitrary_name3 null 

, что я хочу, чтобы поместите имена действий в виде столбцов. Я знаю, что могу сделать это, используя DECODE (так как в 10g нет pivot). но как использовать одно и то же имя действия как имя столбца? Список действий зависит от запроса. (таблица log_table содержит действия для каждого запроса, количество действий является переменной). Надеюсь, мой вопрос достаточно ясен! Благодарю.

+0

В регулярной инструкции SQL имена столбцов являются фиксированными. Если вам нужны динамические имена столбцов, вам нужно использовать динамический SQL. –

+0

Можете ли вы указать мне учебник или что-то в этом роде? (Кстати, вы ответили на мой последний вопрос! спасибо за ваши вклады ^^) – RidRoid

ответ

1

This solution кем-то гораздо умнее, чем я хорошо работает на 10 или 11 с динамическим числом столбцов. Единственное, чего он не делает, это упорядочить столбцы. И реализация

SELECT * 
    FROM TABLE(DYNAMIC_PIVOT(

         'SELECT emp_no,dept_no from emp group by emp_no')); 




CREATE OR REPLACE TYPE PIVOTIMPL AS OBJECT 
(
--from here https://technology.amis.nl/2006/05/24/dynamic-sql-pivoting-stealing-antons-thunder/ 
    RET_TYPE ANYTYPE, -- The return type of the table function 
    STMT  VARCHAR2(32767), 
    FMT  VARCHAR2(32767), 
    CUR  INTEGER, 
    STATIC FUNCTION ODCITABLEDESCRIBE(RTYPE OUT ANYTYPE, 
            P_STMT IN VARCHAR2, 
            P_FMT IN VARCHAR2 := 'upper(@[email protected])', 
            DUMMY IN NUMBER := 0) RETURN NUMBER, 
    STATIC FUNCTION ODCITABLEPREPARE(SCTX OUT PIVOTIMPL, 
            TI  IN SYS.ODCITABFUNCINFO, 
            P_STMT IN VARCHAR2, 
            P_FMT IN VARCHAR2 := 'upper(@[email protected])', 
            DUMMY IN NUMBER := 0) RETURN NUMBER, 
    STATIC FUNCTION ODCITABLESTART(SCTX IN OUT PIVOTIMPL, 
           P_STMT IN VARCHAR2, 
           P_FMT IN VARCHAR2 := 'upper(@[email protected])', 
           DUMMY IN NUMBER := 0) RETURN NUMBER, 
    MEMBER FUNCTION ODCITABLEFETCH(SELF IN OUT PIVOTIMPL, 
           NROWS IN NUMBER, 
           OUTSET OUT ANYDATASET) RETURN NUMBER, 
    MEMBER FUNCTION ODCITABLECLOSE(SELF IN PIVOTIMPL) RETURN NUMBER 
) 
/
prompt 
prompt Creating function DYNAMIC_PIVOT 
prompt =============================== 
prompt 
CREATE OR REPLACE FUNCTION BROO1APP.DYNAMIC_PIVOT(P_STMT IN VARCHAR2, 
                P_FMT IN VARCHAR2 := 'upper(@[email protected])', 
                DUMMY IN NUMBER := 0) 
    RETURN ANYDATASET 
    PIPELINED USING PIVOTIMPL; 
/
prompt 
prompt Creating type body PIVOTIMPL 
prompt ============================ 
prompt 
CREATE OR REPLACE TYPE BODY BROO1APP.PIVOTIMPL AS 
    STATIC FUNCTION ODCITABLEDESCRIBE(RTYPE OUT ANYTYPE, 
            P_STMT IN VARCHAR2, 
            P_FMT IN VARCHAR2 := 'upper(@[email protected])', 
            DUMMY IN NUMBER) RETURN NUMBER IS 
    ATYP  ANYTYPE; 
    CUR  INTEGER; 
    NUMCOLS NUMBER; 
    DESC_TAB DBMS_SQL.DESC_TAB2; 
    RC  SYS_REFCURSOR; 
    T_C2  VARCHAR2(32767); 
    T_FMT VARCHAR2(1000); 
    BEGIN 
    CUR := DBMS_SQL.OPEN_CURSOR; 
    DBMS_SQL.PARSE(CUR, P_STMT, DBMS_SQL.NATIVE); 
    DBMS_SQL.DESCRIBE_COLUMNS2(CUR, NUMCOLS, DESC_TAB); 
    DBMS_SQL.CLOSE_CURSOR(CUR); 
    -- 
    ANYTYPE.BEGINCREATE(DBMS_TYPES.TYPECODE_OBJECT, ATYP); 
    FOR I IN 1 .. NUMCOLS - 2 LOOP 
     ATYP.ADDATTR(DESC_TAB(I).COL_NAME, 
        CASE DESC_TAB(I).COL_TYPE 
        WHEN 1 THEN 
         DBMS_TYPES.TYPECODE_VARCHAR2 
        WHEN 2 THEN 
         DBMS_TYPES.TYPECODE_NUMBER 
        WHEN 9 THEN 
         DBMS_TYPES.TYPECODE_VARCHAR2 
        WHEN 11 THEN 
         DBMS_TYPES.TYPECODE_VARCHAR2 -- show rowid as varchar2 
        WHEN 12 THEN 
         DBMS_TYPES.TYPECODE_DATE 
        WHEN 208 THEN 
         DBMS_TYPES.TYPECODE_VARCHAR2 -- show urowid as varchar2 
        WHEN 96 THEN 
         DBMS_TYPES.TYPECODE_CHAR 
        WHEN 180 THEN 
         DBMS_TYPES.TYPECODE_TIMESTAMP 
        WHEN 181 THEN 
         DBMS_TYPES.TYPECODE_TIMESTAMP_TZ 
        WHEN 231 THEN 
         DBMS_TYPES.TYPECODE_TIMESTAMP_LTZ 
        WHEN 182 THEN 
         DBMS_TYPES.TYPECODE_INTERVAL_YM 
        WHEN 183 THEN 
         DBMS_TYPES.TYPECODE_INTERVAL_DS 
        END, 
        DESC_TAB(I).COL_PRECISION, 
        DESC_TAB(I).COL_SCALE, 
        CASE DESC_TAB(I).COL_TYPE 
        WHEN 11 THEN 
         18 -- for rowid col_max_len = 16, and 18 characters are shown 
        ELSE 
         DESC_TAB(I).COL_MAX_LEN 
        END, 
        DESC_TAB(I).COL_CHARSETID, 
        DESC_TAB(I).COL_CHARSETFORM); 
    END LOOP; 
    IF INSTR(P_FMT, '@[email protected]') > 0 THEN 
     T_FMT := P_FMT; 
    ELSE 
     T_FMT := '@[email protected]'; 
    END IF; 
    OPEN RC FOR REPLACE('select distinct ' || T_FMT || ' 
          from(' || P_STMT || ') 
          order by ' || T_FMT, 
         '@[email protected]', 
         DESC_TAB(NUMCOLS - 1).COL_NAME); 
    LOOP 
     FETCH RC 
     INTO T_C2; 
     EXIT WHEN RC%NOTFOUND; 
     ATYP.ADDATTR(T_C2, 
        CASE  DESC_TAB(NUMCOLS).COL_TYPE WHEN 1 THEN 
        DBMS_TYPES.TYPECODE_VARCHAR2 WHEN 2 THEN 
        DBMS_TYPES.TYPECODE_NUMBER WHEN 9 THEN 
        DBMS_TYPES.TYPECODE_VARCHAR2 WHEN 11 THEN 
        DBMS_TYPES.TYPECODE_VARCHAR2 -- show rowid as varchar2 
        WHEN 12 THEN DBMS_TYPES.TYPECODE_DATE WHEN 208 THEN 
        DBMS_TYPES.TYPECODE_UROWID WHEN 96 THEN 
        DBMS_TYPES.TYPECODE_CHAR WHEN 180 THEN 
        DBMS_TYPES.TYPECODE_TIMESTAMP WHEN 181 THEN 
        DBMS_TYPES.TYPECODE_TIMESTAMP_TZ WHEN 231 THEN 
        DBMS_TYPES.TYPECODE_TIMESTAMP_LTZ WHEN 182 THEN 
        DBMS_TYPES.TYPECODE_INTERVAL_YM WHEN 183 THEN 
        DBMS_TYPES.TYPECODE_INTERVAL_DS END, 
        DESC_TAB(NUMCOLS).COL_PRECISION, 
        DESC_TAB(NUMCOLS).COL_SCALE, 
        CASE  DESC_TAB(NUMCOLS).COL_TYPE WHEN 11 THEN 18 -- for rowid col_max_len = 16, and 18 characters are shown 
        ELSE  DESC_TAB(NUMCOLS).COL_MAX_LEN END, 
        DESC_TAB(NUMCOLS).COL_CHARSETID, 
        DESC_TAB(NUMCOLS).COL_CHARSETFORM); 
    END LOOP; 
    CLOSE RC; 
    ATYP.ENDCREATE; 
    ANYTYPE.BEGINCREATE(DBMS_TYPES.TYPECODE_TABLE, RTYPE); 
    RTYPE.SETINFO(NULL, 
        NULL, 
        NULL, 
        NULL, 
        NULL, 
        ATYP, 
        DBMS_TYPES.TYPECODE_OBJECT, 
        0); 
    RTYPE.ENDCREATE(); 
    RETURN ODCICONST.SUCCESS; 
    EXCEPTION 
    WHEN OTHERS THEN 
     RETURN ODCICONST.ERROR; 
    END; 
    -- 
    STATIC FUNCTION ODCITABLEPREPARE(SCTX OUT PIVOTIMPL, 
            TI  IN SYS.ODCITABFUNCINFO, 
            P_STMT IN VARCHAR2, 
            P_FMT IN VARCHAR2 := 'upper(@[email protected])', 
            DUMMY IN NUMBER) RETURN NUMBER IS 
    PREC  PLS_INTEGER; 
    SCALE PLS_INTEGER; 
    LEN  PLS_INTEGER; 
    CSID  PLS_INTEGER; 
    CSFRM PLS_INTEGER; 
    ELEM_TYP ANYTYPE; 
    ANAME VARCHAR2(30); 
    TC  PLS_INTEGER; 
    BEGIN 
    TC := TI.RETTYPE.GETATTRELEMINFO(1, 
            PREC, 
            SCALE, 
            LEN, 
            CSID, 
            CSFRM, 
            ELEM_TYP, 
            ANAME); 
    -- 
    IF INSTR(P_FMT, '@[email protected]') > 0 THEN 
     SCTX := PIVOTIMPL(ELEM_TYP, P_STMT, P_FMT, NULL); 
    ELSE 
     SCTX := PIVOTIMPL(ELEM_TYP, P_STMT, '@[email protected]', NULL); 
    END IF; 
    RETURN ODCICONST.SUCCESS; 
    END; 
    -- 
    STATIC FUNCTION ODCITABLESTART(SCTX IN OUT PIVOTIMPL, 
           P_STMT IN VARCHAR2, 
           P_FMT IN VARCHAR2 := 'upper(@[email protected])', 
           DUMMY IN NUMBER) RETURN NUMBER IS 
    CUR   INTEGER; 
    NUMCOLS  NUMBER; 
    DESC_TAB DBMS_SQL.DESC_TAB2; 
    T_STMT  VARCHAR2(32767); 
    TYPE_CODE PLS_INTEGER; 
    PREC  PLS_INTEGER; 
    SCALE  PLS_INTEGER; 
    LEN   PLS_INTEGER; 
    CSID  PLS_INTEGER; 
    CSFRM  PLS_INTEGER; 
    SCHEMA_NAME VARCHAR2(30); 
    TYPE_NAME VARCHAR2(30); 
    VERSION  VARCHAR2(30); 
    ATTR_COUNT PLS_INTEGER; 
    ATTR_TYPE ANYTYPE; 
    ATTR_NAME VARCHAR2(100); 
    DUMMY2  INTEGER; 
    FIRST_COL VARCHAR2(30); 
    BEGIN 
    CUR := DBMS_SQL.OPEN_CURSOR; 
    DBMS_SQL.PARSE(CUR, P_STMT, DBMS_SQL.NATIVE); 
    DBMS_SQL.DESCRIBE_COLUMNS2(CUR, NUMCOLS, DESC_TAB); 
    DBMS_SQL.CLOSE_CURSOR(CUR); 
    -- 
    FOR I IN 1 .. NUMCOLS - 2 LOOP 
     T_STMT := T_STMT || ', "' || DESC_TAB(I).COL_NAME || '"'; 
    END LOOP; 
    -- 
    TYPE_CODE := SCTX.RET_TYPE.GETINFO(PREC, 
             SCALE, 
             LEN, 
             CSID, 
             CSFRM, 
             SCHEMA_NAME, 
             TYPE_NAME, 
             VERSION, 
             ATTR_COUNT); 
    FOR I IN NUMCOLS - 1 .. ATTR_COUNT LOOP 
     TYPE_CODE := SCTX.RET_TYPE.GETATTRELEMINFO(I, 
               PREC, 
               SCALE, 
               LEN, 
               CSID, 
               CSFRM, 
               ATTR_TYPE, 
               ATTR_NAME); 
     T_STMT := T_STMT || 
        REPLACE(', max(decode(' || SCTX.FMT || ', ''' || 
          ATTR_NAME || ''', ' || DESC_TAB(NUMCOLS) 
          .COL_NAME || '))', 
          '@[email protected]', 
          DESC_TAB(NUMCOLS - 1).COL_NAME); 
    END LOOP; 
    T_STMT := 'select ' || SUBSTR(T_STMT, 2) || ' from (' || SCTX.STMT || ')'; 
    FOR I IN 1 .. NUMCOLS - 2 LOOP 
     IF I = 1 THEN 
     T_STMT := T_STMT || ' group by "' || DESC_TAB(I).COL_NAME || '"'; 
     FIRST_COL := DESC_TAB(I).COL_NAME; 
     ELSE 
     T_STMT := T_STMT || ', "' || DESC_TAB(I).COL_NAME || '"'; 
     END IF; 
    END LOOP; 
    T_STMT := T_STMT || ' ORDER BY "' || FIRST_COL || '"'; 
    -- 
    DBMS_OUTPUT.PUT_LINE(T_STMT); 
    SCTX.CUR := DBMS_SQL.OPEN_CURSOR; 
    DBMS_SQL.PARSE(SCTX.CUR, T_STMT, DBMS_SQL.NATIVE); 
    FOR I IN 1 .. ATTR_COUNT LOOP 
     TYPE_CODE := SCTX.RET_TYPE.GETATTRELEMINFO(I, 
               PREC, 
               SCALE, 
               LEN, 
               CSID, 
               CSFRM, 
               ATTR_TYPE, 
               ATTR_NAME); 
     CASE TYPE_CODE 
     WHEN DBMS_TYPES.TYPECODE_CHAR THEN 
      DBMS_SQL.DEFINE_COLUMN(SCTX.CUR, I, 'x', 32767); 
     WHEN DBMS_TYPES.TYPECODE_VARCHAR2 THEN 
      DBMS_SQL.DEFINE_COLUMN(SCTX.CUR, I, 'x', 32767); 
     WHEN DBMS_TYPES.TYPECODE_NUMBER THEN 
      DBMS_SQL.DEFINE_COLUMN(SCTX.CUR, I, CAST(NULL AS NUMBER)); 
     WHEN DBMS_TYPES.TYPECODE_DATE THEN 
      DBMS_SQL.DEFINE_COLUMN(SCTX.CUR, I, CAST(NULL AS DATE)); 
     WHEN DBMS_TYPES.TYPECODE_UROWID THEN 
      DBMS_SQL.DEFINE_COLUMN(SCTX.CUR, I, CAST(NULL AS UROWID)); 
     WHEN DBMS_TYPES.TYPECODE_TIMESTAMP THEN 
      DBMS_SQL.DEFINE_COLUMN(SCTX.CUR, I, CAST(NULL AS TIMESTAMP)); 
     WHEN DBMS_TYPES.TYPECODE_TIMESTAMP_TZ THEN 
      DBMS_SQL.DEFINE_COLUMN(SCTX.CUR, I, CAST(NULL AS TIMESTAMP WITH TIME ZONE)); 
     WHEN DBMS_TYPES.TYPECODE_TIMESTAMP_LTZ THEN 
      DBMS_SQL.DEFINE_COLUMN(SCTX.CUR, I, CAST(NULL AS TIMESTAMP WITH LOCAL TIME ZONE)); 
     WHEN DBMS_TYPES.TYPECODE_INTERVAL_YM THEN 
      DBMS_SQL.DEFINE_COLUMN(SCTX.CUR, 
           I, 
           CAST(NULL AS INTERVAL YEAR TO MONTH)); 
     WHEN DBMS_TYPES.TYPECODE_INTERVAL_DS THEN 
      DBMS_SQL.DEFINE_COLUMN(SCTX.CUR, 
           I, 
           CAST(NULL AS INTERVAL DAY TO SECOND)); 
     END CASE; 
    END LOOP; 
    DUMMY2 := DBMS_SQL.EXECUTE(SCTX.CUR); 
    RETURN ODCICONST.SUCCESS; 
    END; 
    -- 
    MEMBER FUNCTION ODCITABLEFETCH(SELF IN OUT PIVOTIMPL, 
           NROWS IN NUMBER, 
           OUTSET OUT ANYDATASET) RETURN NUMBER IS 
    C1_COL_TYPE PLS_INTEGER; 
    TYPE_CODE PLS_INTEGER; 
    PREC  PLS_INTEGER; 
    SCALE  PLS_INTEGER; 
    LEN   PLS_INTEGER; 
    CSID  PLS_INTEGER; 
    CSFRM  PLS_INTEGER; 
    SCHEMA_NAME VARCHAR2(30); 
    TYPE_NAME VARCHAR2(30); 
    VERSION  VARCHAR2(30); 
    ATTR_COUNT PLS_INTEGER; 
    ATTR_TYPE ANYTYPE; 
    ATTR_NAME VARCHAR2(100); 
    V1   VARCHAR2(32767); 
    N1   NUMBER; 
    D1   DATE; 
    UR1   UROWID; 
    IDS1  INTERVAL DAY TO SECOND; 
    IYM1  INTERVAL YEAR TO MONTH; 
    TS1   TIMESTAMP; 
    TSTZ1  TIMESTAMP WITH TIME ZONE; 
    TSLTZ1  TIMESTAMP WITH LOCAL TIME ZONE; 
    BEGIN 
    OUTSET := NULL; 
    IF NROWS < 1 THEN 
     -- is this possible??? 
     RETURN ODCICONST.SUCCESS; 
    END IF; 
    -- 
    DBMS_OUTPUT.PUT_LINE('fetch'); 
    IF DBMS_SQL.FETCH_ROWS(SELF.CUR) = 0 THEN 
     RETURN ODCICONST.SUCCESS; 
    END IF; 
    -- 
    DBMS_OUTPUT.PUT_LINE('done'); 
    TYPE_CODE := SELF.RET_TYPE.GETINFO(PREC, 
             SCALE, 
             LEN, 
             CSID, 
             CSFRM, 
             SCHEMA_NAME, 
             TYPE_NAME, 
             VERSION, 
             ATTR_COUNT); 
    ANYDATASET.BEGINCREATE(DBMS_TYPES.TYPECODE_OBJECT, 
          SELF.RET_TYPE, 
          OUTSET); 
    OUTSET.ADDINSTANCE; 
    OUTSET.PIECEWISE(); 
    FOR I IN 1 .. ATTR_COUNT LOOP 
     TYPE_CODE := SELF.RET_TYPE.GETATTRELEMINFO(I, 
               PREC, 
               SCALE, 
               LEN, 
               CSID, 
               CSFRM, 
               ATTR_TYPE, 
               ATTR_NAME); 
     DBMS_OUTPUT.PUT_LINE(ATTR_NAME); 
     CASE TYPE_CODE 
     WHEN DBMS_TYPES.TYPECODE_CHAR THEN 
      DBMS_SQL.COLUMN_VALUE(SELF.CUR, I, V1); 
      OUTSET.SETCHAR(V1); 
     WHEN DBMS_TYPES.TYPECODE_VARCHAR2 THEN 
      DBMS_SQL.COLUMN_VALUE(SELF.CUR, I, V1); 
      OUTSET.SETVARCHAR2(V1); 
     WHEN DBMS_TYPES.TYPECODE_NUMBER THEN 
      DBMS_SQL.COLUMN_VALUE(SELF.CUR, I, N1); 
      OUTSET.SETNUMBER(N1); 
     WHEN DBMS_TYPES.TYPECODE_DATE THEN 
      DBMS_SQL.COLUMN_VALUE(SELF.CUR, I, D1); 
      OUTSET.SETDATE(D1); 
     WHEN DBMS_TYPES.TYPECODE_UROWID THEN 
      DBMS_SQL.COLUMN_VALUE(SELF.CUR, I, UR1); 
      OUTSET.SETUROWID(UR1); 
     WHEN DBMS_TYPES.TYPECODE_INTERVAL_DS THEN 
      DBMS_SQL.COLUMN_VALUE(SELF.CUR, I, IDS1); 

      OUTSET.SETINTERVALDS(IDS1); 
     WHEN DBMS_TYPES.TYPECODE_INTERVAL_YM THEN 
      DBMS_SQL.COLUMN_VALUE(SELF.CUR, I, IYM1); 
      OUTSET.SETINTERVALYM(IYM1); 
     WHEN DBMS_TYPES.TYPECODE_TIMESTAMP THEN 
      DBMS_SQL.COLUMN_VALUE(SELF.CUR, I, TS1); 
      OUTSET.SETTIMESTAMP(TS1); 
     WHEN DBMS_TYPES.TYPECODE_TIMESTAMP_TZ THEN 
      DBMS_SQL.COLUMN_VALUE(SELF.CUR, I, TSTZ1); 
      OUTSET.SETTIMESTAMPTZ(TSTZ1); 
     WHEN DBMS_TYPES.TYPECODE_TIMESTAMP_LTZ THEN 
      DBMS_SQL.COLUMN_VALUE(SELF.CUR, I, TSLTZ1); 
      OUTSET.SETTIMESTAMPLTZ(TSLTZ1); 
     END CASE; 
    END LOOP; 
    OUTSET.ENDCREATE; 
    RETURN ODCICONST.SUCCESS; 
    END; 
    -- 
    MEMBER FUNCTION ODCITABLECLOSE(SELF IN PIVOTIMPL) RETURN NUMBER IS 
    C INTEGER; 
    BEGIN 
    C := SELF.CUR; 
    DBMS_SQL.CLOSE_CURSOR(C); 
    RETURN ODCICONST.SUCCESS; 
    END; 
END; 
/
+0

Это очень хорошая функция! Благодарю. Я попытаюсь найти обходной путь для заказа столбцов (это важно в моем случае). Если у вас есть идея помочь, это будет здорово! – RidRoid

+0

@RidRoid В коде я попытался сделать это, заказав первый столбец из выбранного. См. Инструкцию по подготовке. Это не сработало для одного из моих запросов, где я связывал имена строк в столбцах для прямого выбора, который должен делать – kevinsky

+0

вы можете поделиться со мной своим редактированием, пожалуйста? Я не заметил разницу между вашей функцией и оригиналом (из блога) – RidRoid

Смежные вопросы