2014-11-24 44 views
1

我有set s由type組織。我想查找所有set的唯一組合,從每個type取一個set。所以,我開始與此:SQL:按類型組合

table1: 
row_id type set 
1  a  1 
2  a  2 
3  a  3 
4  b  4 
5  b  5 
6  c  6 

,並希望得到這樣的:

table2: 
row_id combo_id type set 
1  1   a  1 
2  1   b  4 
3  1   c  6 
4  2   a  2 
5  2   b  4 
6  2   c  6 
7  3   a  3 
8  3   b  4 
9  3   c  6 
10  4   a  1 
11  4   b  5 
12  4   c  6 
13  5   a  2 
14  5   b  5 
15  5   c  6 
16  6   a  3 
17  6   b  5 
18  6   c  6 

第一個想法可能是使用CROSS JOIN和得到的東西是這樣的:

table3: 
row_id combo_id a_set b_set c_set 
1  1   1  4  6 
2  2   2  4  6 
3  3   3  4  6 
4  4   1  5  6 
5  5   2  5  6 
6  6   3  5  6 

然而,我真實數據有成千上萬的type s,沒有該數字的上限,所以我認爲table2中的設置是必要的。

我看到有很多關於SQL組合的Stack Overflow問題。然而,我發現沒有發現通過type排序,更不用說無限數量的type s。

我正在使用PLSQL Developer與Oracle 10g。謝謝!在您的表2表示到達

+1

如果你有成千上萬的類型,也沒有辦法(至少在這個宇宙中)你可以列出所有這些 - 除了在幾乎所有隻有一行的微不足道的情況下。你能更好地描述你真正的問題嗎? – 2014-11-24 15:50:30

+0

嗨@GordonLinoff,這是我真正的問題,只有更少的'類型'。我確實有成千上萬個'類型',但它們每個只有幾行。 – esa606 2014-11-24 15:52:50

+1

。 。如果你有1000行,每行有2行,你可以有2^1000個不同的組合。宇宙中的原子數估計在2^272左右。大爆炸以來的毫秒數估計更像2^68毫秒。因此,我對宇宙的評論以及你的實際問題可能是什麼。 – 2014-11-24 21:41:55

回答

1

一種方法是使用一個向上和向下的分級查詢:

with table1(row_id, type, val) as (
select 1, 'a', 1 from dual union all 
select 2, 'a', 2 from dual union all 
select 3, 'a', 3 from dual union all 
select 4, 'b', 4 from dual union all 
select 5, 'b', 5 from dual union all 
select 6, 'c', 6 from dual 
), t2 as (
select row_id 
    , type 
    , DENSE_RANK() OVER (ORDER BY type desc) type_id 
    , val 
    from table1 
), Up as (
select row_number() over (partition by CONNECT_BY_ISLEAF 
           order by SYS_CONNECT_BY_PATH(row_id, ',')) combo_id 
    , SYS_CONNECT_BY_PATH(row_id, ',') path_id 
    , prior SYS_CONNECT_BY_PATH(row_id, ',') parent_path_id 
    , t2.* 
    , CONNECT_BY_ISLEAF leaf 
    from t2 
connect by type_id = prior type_id+1 
start with type_id = 1 
), Down as (
select row_number() over (order by CONNECT_BY_ROOT combo_id, type) row_id 
    , CONNECT_BY_ROOT combo_id combo_id 
    , type 
    , val 
    from Up 
    connect by path_id = prior parent_path_id 
    start with leaf = 1 
) 
select * from Down; 

在這個解決方案的子查詢t2會爲每個獨特type我已經下令他們連續的ID以相反的順序,以便第一次遍歷將以type'a'作爲葉節點。

在第一樹遍歷(子查詢Up)我添加了一個獨特的組合代碼,所有的葉記錄分組的目的,添加在接下來的樹遍歷使用path_idparent_path_id列。這也是所有新行生成的階段。

在第二樹遍歷(子查詢Down)我在從Up遍歷葉節點開始和爬回到下降到在先前遍歷生成的根保持根(葉?)combo_id。在這個階段沒有生成額外的行,因爲它是從葉子到樹根的直接鏡頭。

最終結果:

ROW_ID COMBO_ID T  VAL 
-------- ---------- - ---------- 
     1   1 a   1 
     2   1 b   4 
     3   1 c   6 
     4   2 a   2 
     5   2 b   4 
     6   2 c   6 
     7   3 a   3 
     8   3 b   4 
     9   3 c   6 
     10   4 a   1 
     11   4 b   5 
     12   4 c   6 
     13   5 a   2 
     14   5 b   5 
     15   5 c   6 
     16   6 a   3 
     17   6 b   5 
     18   6 c   6 

18 rows selected 

如果你希望你的表3表示可以更改select * from Down這樣:

select row_number() over (order by combo_id) row_id 
    , pvt.* 
    from (select combo_id, type, val from Down) 
pivot (max(val) "SET" 
    for (type) in ('a' A 
       ,'b' B 
       ,'c' C)) pvt; 

,得到以下結果:

ROW_ID COMBO_ID  A_SET  B_SET  C_SET 
---------- ---------- ---------- ---------- ---------- 
     1   1   1   4   6 
     2   2   2   4   6 
     3   3   3   4   6 
     4   4   1   5   6 
     5   5   2   5   6 
     6   6   3   5   6 
+0

有趣!很老的問題,但很高興看到答案 – esa606 2015-07-29 21:59:40

+0

@ esa606感謝這是一個很好的練習。我已經添加了一個數據透視表,以便從原始問題中獲取最終的table3佈局,並修正了Combo_ID的排列順序以獲得完全相同的輸出:) – Sentinel 2015-07-29 22:32:16

0

SQL Fiddle

的Oracle 11g R2架構設置

CREATE TABLE table1 ("rowid", "type", "set") AS 
      SELECT 1, 'a', 1 FROM DUAL 
UNION ALL SELECT 2, 'a', 2 FROM DUAL 
UNION ALL SELECT 3, 'a', 3 FROM DUAL 
UNION ALL SELECT 4, 'b', 4 FROM DUAL 
UNION ALL SELECT 5, 'b', 5 FROM DUAL 
UNION ALL SELECT 6, 'c', 6 FROM DUAL 
// 
CREATE TYPE combo_sets AS OBJECT(
    "type" CHAR(1), 
    idx NUMBER(5,0), 
    sets SYS.ODCINUMBERLIST 
); 
// 
CREATE TYPE t_combo_sets AS TABLE OF combo_sets; 
// 
CREATE TYPE combo AS OBJECT(
    "rowid" NUMBER(8,0), 
    "comboid" NUMBER(8,0), 
    "type"  CHAR(1), 
    "set"  NUMBER(5,0) 
); 
// 
CREATE TYPE t_combos AS TABLE of combo; 
// 
CREATE OR REPLACE FUNCTION get_combos 
RETURN t_combos PIPELINED 
AS 
    v_combo_sets t_combo_sets; 
    i   NUMBER(5,0); 
    r   NUMBER(5,0) := 1; 
    c   NUMBER(5,0) := 1; 
BEGIN 
    SELECT combo_sets( 
      "type", 
      1, 
      CAST(COLLECT("set" ORDER BY "set") AS SYS.ODCINUMBERLIST) 
     ) 
    BULK COLLECT INTO v_combo_sets 
    FROM table1 
    GROUP BY "type"; 

    i := 1; 
    WHILE i <= v_combo_sets.COUNT LOOP 
    FOR j IN 1 .. v_combo_sets.COUNT LOOP 
     PIPE ROW(
     combo(
      r, 
      c, 
      v_combo_sets(j)."type", 
      v_combo_sets(j).sets(v_combo_sets(j).idx) 
     ) 
    ); 
     r := r + 1; 
    END LOOP; 
    c := c + 1; 

    i := 1; 
    WHILE i <= v_combo_sets.COUNT AND v_combo_sets(i).idx = v_combo_sets(i).sets.COUNT LOOP  
     v_combo_sets(i).idx := 1; 
     i := i + 1; 
    END LOOP; 
    IF i <= v_combo_sets.COUNT THEN 
     v_combo_sets(i).idx := v_combo_sets(i).idx + 1; 
    END IF; 
    END LOOP; 

    RETURN; 
END; 
// 

查詢1

SELECT * 
FROM TABLE(get_combos) 

Results

| rowid | comboid | type | set | 
|-------|---------|------|-----| 
|  1 |  1 | a | 1 | 
|  2 |  1 | b | 4 | 
|  3 |  1 | c | 6 | 
|  4 |  2 | a | 2 | 
|  5 |  2 | b | 4 | 
|  6 |  2 | c | 6 | 
|  7 |  3 | a | 3 | 
|  8 |  3 | b | 4 | 
|  9 |  3 | c | 6 | 
| 10 |  4 | a | 1 | 
| 11 |  4 | b | 5 | 
| 12 |  4 | c | 6 | 
| 13 |  5 | a | 2 | 
| 14 |  5 | b | 5 | 
| 15 |  5 | c | 6 | 
| 16 |  6 | a | 3 | 
| 17 |  6 | b | 5 | 
| 18 |  6 | c | 6 |