Oracle - 将多个行合并为一个,使用列值作为行名称,动态

时间:2016-03-11 12:23:04

标签: oracle dynamic pivot

我正在尝试将多行合并为一行。列值需要成为列名,并且需要是动态的,因为可能会添加更多列值

当前

key attribute   value   reason  message             
1001    Att1    Val1    Reason 1    Message 1               
1001    Att2    Val2    Reason 1    Message 1               
1001    Att3    Val3    Reason 1    Message 1               
1002    Att1    Val4    Reason 2    Message 2               
1002    Att2    Val5    Reason 2    Message 2               
1002    Att4    Val7    Reason 2    Message 2               
1002    Att5    Val8    Reason 2    Message 2               
1002    Att6    Val9    Reason 2    Message 2       

想要这个结构

key     Att1    Att2    Att3    Att4    Att5    Att6    reason      message
1001    Val1    Val2    Val3                            Reason 1    Message 1
1002    Val4    Val5            Val7    Val8    Val9    Reason 2    Message 2

请帮忙!

2 个答案:

答案 0 :(得分:0)

要获得结果,您需要进行PIVOT操作;主要问题是你需要它是动态的,所以我们事先并不知道你会得到的数字或列数。

您可以尝试使用一些动态SQL,构建一个与您的数据匹配的语句:

declare
    vSQL varchar2(32767) :=
                            ' from (select * from test)
                              pivot (max(value) for attribute in (';
    vAttributes varchar2(32767);
    vOutputFields varchar2(32767) := 'key || '' | '' || reason || '' | '' || message || '' | '' || ';
    vAllFields varchar2(3) := '*';
    vClob clob;
    type tabVarchar2 is table of varchar2(32767);
    vTabVarchar2 tabVarchar2;
    vSQLOneField varchar2(32767);
    vSQLMoreFields varchar2(32767);
begin
    -- get the columns
    select listagg( '''' || attribute || '''', ',') within group (order by attribute),
     vOutputFields || listagg( '"''' || attribute || '''"', '|| '' | '' || ') within group (order by attribute)
    into vAttributes, vOutputFields
    from (select distinct attribute from test) ;

    -- build the statement for many fields
    vSQLMoreFields := 'select ' || vAllFields || vSQL || vAttributes || '))';
    -- print the query
    dbms_output.put_line(vSQLMoreFields);
    -- fetch it as a unique XML
    select DBMS_XMLGEN.getXML(vSQLMoreFields)
    into vClob
    from dual;   
    dbms_output.put_line(vClob);

    -- build the statement for a unique field
    vSQLOneField := 'select ' || vOutputFields || ' from (' || vSQLMoreFields || ')';
    -- print the query
    dbms_output.put_line(vSQLOneField);
    -- fetch as a unique field into a structure
    execute immediate vSQLOneField bulk collect into vTabVarchar2;
    for i in 1 .. vTabVarchar2.last loop
        dbms_output.put_line( vTabVarchar2(i));
    end loop;

end;

我主要看到两个问题:

  • 很难找到一种方法来结果并使用它;我将示例提取到一个唯一的XML中,带有单独的字段,或者成为varchar2的数组,每个值包含整行;
  • 我访问该表两次,一次获取列,一次获取实际数据,这可能是性能问题

答案 1 :(得分:0)

另一种方法是这个:

CREATE OR REPLACE FUNCTION TransposeTab RETURN SYS_REFCURSOR AS
    cur SYS_REFCURSOR;
    sqlstr VARCHAR2(10000);
BEGIN
    sqlstr := 'SELECT * FROM TABLE1 PIVOT (MAX(val) FOR ATTRIBUTE IN (';
    FOR aCol IN (SELECT DISTINCT ATTRIBUTE FROM TABLE1) LOOP
        sqlstr := sqlstr || ''''||aCol.ATTRIBUTE||''' AS "'||aCol.ATTRIBUTE||'",';
    END LOOP;
    sqlstr := REGEXP_REPLACE(sqlstr, ',$','))');
    --DBMS_OUTPUT.PUT_LINE ( sqlstr );
    OPEN cur FOR sqlstr;
    RETURN cur;
END;

为了得到结果,你可以做到这一点。

SELECT TransposeTab FROM dual;

或(仅限SQL * Plus)

var rc refcursor
EXEC :rc := TransposeTab;
PRINT rc