Oracle 动态连接(或移动行)

Oracle dynamic join (or move row)

我是运行 Oracle 11g,需要将某些行分成列,但列数未确定,所以我需要动态地进行。

最初我想分成多个查询,然后再加入它们,但它似乎不是最合适的,而且我无法使其动态化。

这是我的 table:

的简化示例
CREATE TABLE foo (
    id NUMBER,
    cod NUMBER,
    val NUMBER,
    dat DATE
);

INSERT INTO foo VALUES(1, 35, 58.10, TO_DATE('01-07-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(2, 45, 38.50, TO_DATE('01-07-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(3, 45, 3.89, TO_DATE('20-07-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(4, 35, 102.0, TO_DATE('01-07-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(5, 75, 69.32, TO_DATE('01-07-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(6, 75, 74.65, TO_DATE('01-07-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(7, 45, 32.8, TO_DATE('01-07-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(8, 75, 12.76, TO_DATE('01-07-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(1, 35, 38.50, TO_DATE('01-08-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(2, 45, 3.89, TO_DATE('01-08-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(3, 45, 102.0, TO_DATE('01-08-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(4, 35, 69.32, TO_DATE('01-08-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(5, 75, 74.65, TO_DATE('01-08-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(6, 75, 32.8, TO_DATE('01-08-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(7, 45, 38.50, TO_DATE('30-08-2019', 'DD-MM-YYYY'));
INSERT INTO foo VALUES(8, 75, 3.89, TO_DATE('01-08-2019', 'DD-MM-YYYY'));

SELECT a.cod, a.val, b.cod, b.val 
FROM foo a, (
    SELECT id, cod, val
    FROM foo
    WHERE dat = TO_DATE('01-07-2019', 'DD-MM-YYYY')
) b
WHERE a.id = b.id AND 
    a.dat = TO_DATE('01-08-2019', 'DD-MM-YYYY');

产量(按 COD 排序):

COD|VAL  |COD|VAL  |
---|-----|---|-----|
 35| 38.5| 35| 58.1|
 35|69.32| 35|  102|
 45| 3.89| 45| 38.5|
 75|74.65| 75|69.32|
 75| 32.8| 75|74.65|
 75| 3.89| 75|12.76|

我需要这些行只有一个不同的 COD,其他的将排列在新的列中。

预计:

      COD 35       |      COD 45       |      COD 75       | <- illustrative
-------------------|-------------------|-------------------|
COD|VAL  |COD|VAL  |COD|VAL  |COD|VAL  |COD|VAL  |COD|VAL  |
---|-----|---|-----|---|-----|---|-----|---|-----|---|-----|
 35| 38.5| 35| 58.1| 45| 3.89| 45| 38.5| 75|74.65| 75|69.32|
 35|69.32| 35|  102|   |     |   |     | 75| 32.8| 75|74.65|
   |     |   |     |   |     |   |     | 75| 3.89| 75|12.76|

谢谢

我解决了以下问题:

DECLARE
  p_cod_str VARCHAR2(100) := '35, 75, 45';
  i_prv VARCHAR2(10);
  q_loop VARCHAR2(1000);
  q_select VARCHAR2(1000);
  q_where VARCHAR2(1000);
  q_from VARCHAR2(5000);
  q_query VARCHAR2(5000);
BEGIN
    -- split cods string by comma
    FOR i IN (
            SELECT trim(regexp_substr(p_cod_str, '[^,]+', 1, LEVEL)) cod
            FROM dual
            CONNECT BY LEVEL <= regexp_count(p_cod_str, ',') + 1
    ) LOOP
        q_loop := 'SELECT rownum as rn, a.id as id, 
            a.cod AS cod_a_' || i.cod || ', a.val AS val_a_' || i.cod || ', 
            b.cod AS cod_b_' || i.cod || ', b.val AS val_b_' || i.cod || ' 
            FROM foo a, (
                SELECT id, cod, val
                FROM foo
                WHERE dat = TO_DATE(''01-07-2019'', ''DD-MM-YYYY'')
            ) b
            WHERE a.id = b.id AND a.cod = ' || i.cod || ' AND
                a.dat = TO_DATE(''01-08-2019'', ''DD-MM-YYYY'')
        ';

        q_select := q_select || ', 
            t_' || i.cod || '.cod_a_' || i.cod || ', t_' || i.cod || '.val_a_' || i.cod || ', 
            t_' || i.cod || '.cod_b_' || i.cod || ', t_' || i.cod || '.val_b_' || i.cod;

        IF q_from IS NULL THEN
            q_from := ' (
                ' || q_loop || '
            ) t_' || i.cod;
        ELSE
            q_from := q_from || '
            FULL OUTER JOIN (
                ' || q_loop || '
            ) t_' || i.cod || '
            ON t_' || i_prv || '.rn = t_' || i.cod || '.rn';
        END IF;

        i_prv := i.cod;
    END LOOP;

    -- mount query
    q_query := 'SELECT ' || ltrim(q_select, ', ') 
        || ' FROM ' || q_from;

    dbms_output.put_line('query: ' || q_query);
END;

它将生成 SQL 并且 EXECUTE IMMEDIATE 我可以 运行 它。

结果:

COD_A_35|VAL_A_35|COD_B_35|VAL_B_35|COD_A_75|VAL_A_75|COD_B_75|VAL_B_75|COD_A_45|VAL_A_45|COD_B_45|VAL_B_45|
--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|--------|
      35|    38.5|      35|    58.1|      75|   74.65|      75|   69.32|      45|    3.89|      45|    38.5|
      35|   69.32|      35|     102|      75|    32.8|      75|   74.65|        |        |        |        |
        |        |        |        |      75|    3.89|      75|   12.76|        |        |        |        |

感谢大家的提示