Dynamic pivot in oracle sql

后端 未结 7 962

... pivot (sum(A) for B in (X))

Now B is of datatype varchar2 and X is a string of varchar2 values separated by commas.
Values for X are select distinct values

相关标签:
7条回答
  • 2020-11-22 00:12

    For later readers, here is another solution https://technology.amis.nl/2006/05/24/dynamic-sql-pivoting-stealing-antons-thunder/

    allowing a query like

    select * from table( pivot(  'select deptno,  job, count(*) c from scott.emp group by deptno,job' ) )
    
    0 讨论(0)
  • 2020-11-22 00:15

    I used the above method (Anton PL/SQL custom function pivot()) and it done the job! As I am not a professional Oracle developer, these are simple steps I've done:

    1) Download the zip package to find pivotFun.sql in there. 2) Run once the pivotFun.sql to create a new function 3) Use the function in normal SQL.

    Just be careful with dynamic columns names. In my environment I found that column name is limited with 30 characters and cannot contain a single quote in it. So, my query is now something like this:

    SELECT 
      *
    FROM   
      table( 
            pivot('
                    SELECT DISTINCT
                        P.proj_id,
                        REPLACE(substr(T.UDF_TYPE_LABEL, 1, 30), '''''''','','') as Attribute,
                        CASE
                          WHEN V.udf_text is null     and V.udf_date is null and      V.udf_number is NOT null  THEN to_char(V.udf_number)
                          WHEN V.udf_text is null     and V.udf_date is NOT null and  V.udf_number is null      THEN to_char(V.udf_date)
                          WHEN V.udf_text is NOT null and V.udf_date is null and      V.udf_number is null      THEN V.udf_text
                          ELSE NULL END
                        AS VALUE
                    FROM
                        project   P
                    LEFT JOIN UDFVALUE V ON P.proj_id     = V.proj_id 
                    LEFT JOIN UDFTYPE  T ON V.UDF_TYPE_ID = T.UDF_TYPE_ID
                    WHERE 
                        P.delete_session_id  IS NULL AND
                        T.TABLE_NAME = ''PROJECT''
        ')
    )
    

    Works well with up to 1m records.

    0 讨论(0)
  • 2020-11-22 00:18

    You can't put a non constant string in the IN clause of the pivot clause.
    You can use Pivot XML for that.

    From documentation:

    subquery A subquery is used only in conjunction with the XML keyword. When you specify a subquery, all values found by the subquery are used for pivoting

    It should look like this:

    select xmlserialize(content t.B_XML) from t_aa
    pivot xml(
    sum(A) for B in(any)
    ) t;
    

    You can also have a subquery instead of the ANY keyword:

    select xmlserialize(content t.B_XML) from t_aa
    pivot xml(
    sum(A) for B in (select cl from t_bb)
    ) t;
    

    Here is a sqlfiddle demo

    0 讨论(0)
  • 2020-11-22 00:19

    You cannot put a dynamic statement in the PIVOT's IN statement without using PIVOT XML, which outputs some less than desirable output. However, you can create an IN string and input it into your statement.

    First, here is my sample table;

      myNumber    myValue myLetter
    ---------- ---------- --------
             1          2 A        
             1          4 B        
             2          6 C        
             2          8 A        
             2         10 B        
             3         12 C        
             3         14 A      
    

    First setup the string to use in your IN statement. Here you are putting the string into "str_in_statement". We are using COLUMN NEW_VALUE and LISTAGG to setup the string.

    clear columns
    COLUMN temp_in_statement new_value str_in_statement
    SELECT DISTINCT 
        LISTAGG('''' || myLetter || ''' AS ' || myLetter,',')
            WITHIN GROUP (ORDER BY myLetter) AS temp_in_statement 
        FROM (SELECT DISTINCT myLetter FROM myTable);
    

    Your string will look like:

    'A' AS A,'B' AS B,'C' AS C
    

    Now use the String statement in your PIVOT query.

    SELECT * FROM 
        (SELECT myNumber, myLetter, myValue FROM myTable)
        PIVOT (Sum(myValue) AS val FOR myLetter IN (&str_in_statement));
    

    Here is the Output:

      MYNUMBER      A_VAL      B_VAL      C_VAL
    ---------- ---------- ---------- ----------
             1          2          4            
             2          8         10          6 
             3         14                    12 
    

    There are limitations though. You can only concatenate a string up to 4000 bytes.

    0 讨论(0)
  • 2020-11-22 00:24

    You cannot put a dynamic statement in the PIVOT's IN statement without using PIVOT XML, but you can use small Technic to use dynamic statement in PIVOT. In PL/SQL, within a string value, two apostrophe is equal to one apostrophes.

    declare
      sqlqry clob;   
      search_ids  varchar(256) := '''2016'',''2017'',''2018'',''2019''';
    begin
      search_ids := concat( search_ids,'''2020''' ); -- you can append new search id dynamically as you wanted
      sqlqry :=
      '      
      select * from
      (
          select *
          from EMPLOYEE
      )
      pivot
      (
        MIN(QTY) for YR in (' || search_ids   || ')
      )';
    
      execute immediate sqlqry;
    end;
    
    0 讨论(0)
  • 2020-11-22 00:31

    I am not exactly going to give answer for the question OP has asked, instead I will be just describing how dynamic pivot can be done.

    Here we have to use dynamic sql, by initially retrieving the column values into a variable and passing the variable inside dynamic sql.

    EXAMPLE

    Consider we have a table like below.

    If we need to show the values in the column YR as column names and the values in those columns from QTY, then we can use the below code.

    declare
      sqlqry clob;
      cols clob;
    begin
      select listagg('''' || YR || ''' as "' || YR || '"', ',') within group (order by YR)
      into   cols
      from   (select distinct YR from EMPLOYEE);
    
    
      sqlqry :=
      '      
      select * from
      (
          select *
          from EMPLOYEE
      )
      pivot
      (
        MIN(QTY) for YR in (' || cols  || ')
      )';
    
      execute immediate sqlqry;
    end;
    /
    

    RESULT

    If required, you can also create a temp table and do a select query in that temp table to see the results. Its simple, just add the CREATE TABLE TABLENAME AS in the above code.

    sqlqry :=
    '    
      CREATE TABLE TABLENAME AS
      select * from
    
    0 讨论(0)
提交回复
热议问题