【问题标题】:Challenging Oracle PL/SQL - Permutations of assigning one column to another sorted column具有挑战性的 Oracle PL/SQL - 将一列分配给另一列的排列
【发布时间】:2016-07-27 22:21:36
【问题描述】:

输入是G系列和P系列两个数据集,是真实数据中的字符串。

X = 3,

G series
G1
G2
G3

Y = 2,

P series
P1
P2

G 系列的 X 数量始终 > = P 系列的 Y 数量。

我想将所有可能匹配的可用 P 值返回到 G 值,但所有相同的 P 值都分配给 G。在每个组中,G 值的序列是固定的,始终是 G1、G2、G3。

预期结果之一:

    1   G1  P1
    1   G2  P1
    1   G3  P2
---------------
    2   G1  P2
    2   G2  P2
    2   G3  P1
---------------
    3   G1  P1
    3   G2  P2
    3   G3  P1
---------------
    4   G1  P2
    4   G2  P1
    4   G3  P2
---------------
    5   G1  P1
    5   G2  P2
    5   G3  P2
---------------
    6   G1  P2
    6   G2  P1
    6   G3  P1

如您所见,对于 X = 3、Y = 2,我想要 6 或 X*Y 组可能的配对排列:

   | Group 1 | Group 2 | Group 3 | Group 4 | Group 5 | Group 6 |
    -----------------------------------------------------------
   | G1 P1   | G1   P2 | G1 P1   | G1  P2  | G1 P1   | G1 P2   |
   | G2 P1   | G2   P2 | G2 P2   | G2  P1  | G2 P2   | G2 P1   |
   | G3 P2   | G3   P1 | G3 P1   | G3  P2  | G3 P2   | G3 P1   |

P 值到 G1 - G3 的模式: 换句话说,需要像 112,221,121,212,122,211 这样的 P 组合。 但是不需要 111、222 个 P 值。对六组的任何排序方式都开放,只要所有排列都列出。

由于所有相同的 P 都分配给同一组中的 G,因此不需要以下组合:

 1   G1 P1
 1   G2 P1
 1   G3 P1
-----------
 2   G1 P2
 2   G2 P2
 2   G3 P2

我的计划: 先做两个数据集的笛卡尔积:

1   G1  P1
2   G1  P2
3   G2  P1
4   G2  P2
5   G3  P1
6   G3  P2

然后插入 X 个笛卡尔积,并希望创建笛卡尔积的组合序列以获得预期结果,但我无法从中找到模式。

1   G1  P1
2   G2  P1
3   G3  P2
4   G1  P2
5   G2  P2
6   G3  P1
7   G1  P1
8   G2  P2
9   G3  P1
10  G1  P2
11  G2  P1
12  G3  P2
13  G1  P1
14  G2  P2
15  G3  P2
16  G1  P2
17  G2  P1
18  G3  P1

另外,您可能会注意到可能的结果有 X * (Y^X - Y) 行,在上述情况下,当 X = 3 和 Y = 2 时,它是 3*(2^3-2) = 18 行和 6组或 (Y^X - Y)。

此过程会创建不需要的序列:

Create or replace Procedure Permutation ( X in Number, Y in Number )                                                                        
AS j Number:=1;
Begin                                                                                                                               
For k in 1 .. X
   Loop
         For i in 1 .. X*Y                                                          
      Loop                                                          
                CASE 
          WHEN i = 1  THEN
            Insert into Table_1 ("INJECTION") values ( j );                                                                 
            Commit;
          WHEN mod(i,2)= 0 Then
            j :=  j + X ;                                                       
            Insert into Table_1 ("INJECTION") values ( j );                                                     
            Commit;
          WHEN mod(i,2)<>0 then
            j :=  j - Y ;                                                       
            Insert into Table_1 ("INJECTION") values ( j );                                                     
            Commit; 
          End CASE;
            End Loop;                                                           
         j := j + 1;                                                                
    Commit;                                                             
    End Loop;                                                                   
EXCEPTION                                                                       
   WHEN OTHERS                                                                      
   THEN                                                                     
      null;                                                                     
End;

结果:1​​,4,2,5,3,6,7,10,8,11,9,12,13,16,14,17,15,18

1   G1  P1  1
2   G1  P2  4
3   G2  P1  2
4   G2  P2  5
5   G3  P1  3
6   G3  P2  6
7   G1  P1  7
8   G1  P2  10
9   G2  P1  8
10  G2  P2  11
11  G3  P1  9
12  G3  P2  12
13  G1  P1  13
14  G1  P2  16
15  G2  P1  14
16  G2  P2  17
17  G3  P1  15
18  G3  P2  18

对你有以下的数字进行排序,这正是不想要的结果:

1   G1  P1  1
3   G2  P1  2
5   G3  P1  3
2   G1  P2  4
4   G2  P2  5
6   G3  P2  6
7   G1  P1  7
9   G2  P1  8
11  G3  P1  9
8   G1  P2  10
10  G2  P2  11
12  G3  P2  12
13  G1  P1  13
15  G2  P1  14
17  G3  P1  15
14  G1  P2  16
16  G2  P2  17
18  G3  P2  18

我想知道是否有更好的方法来处理这个问题? 数组?等级?

感谢您的投入,非常感谢。


更新:

我在考虑算法,本质上是关于从 一个P数组[P1,P2,P3,..PY] 进入G数组[G1,G2,G3...GX]

Python 3.5.2, 

Create When X = 3, Y = 2, p = [1,2]

   import itertools
   for product in itertools.product([1,2],repeat=3):
        print (product)

(1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 2, 2)
(2, 1, 1)
(2, 1, 2)
(2, 2, 1)
(2, 2, 2)

这正是我想要的结果,除了所有重复项,(1,1,1) 和 (2,2,2)...

【问题讨论】:

  • 您需要明确您的要求。您预期结果的前五行与不想要的组合的前五行完全相同。这两个列表有何不同?
  • 感谢您的评论,克里斯蒂安。如果您再看一遍,可能会注意到 Not Wanted 结果与同一组中的 G1、G2、G3 具有相同的 P 值,例如 G1P1、G2P1、G3P1 和 G1P2、G2P2、G3P2。预期结果是 G1P1、G2P1、G3P2 等组;组内的 P 值并不完全相同。
  • 也许您需要定义组的含义?我看到诸如 G1P1 和 G2P1 之类的配对在通缉名单和不通缉名单中都出现了不止一次,您的问题中没有任何内容可以解释这种情况。
  • 是的,我刚刚修改了问题,希望它更容易理解。在每个组中,所有 G 值都按固定顺序 G1、G2、G3,我想将 P 值与每个 G 值配对,不包括 G1 P1、G2 P1、G3 P1 之类的配对。和 G1 P2、G2 P2、G3 P2。谢谢你的耐心,克里斯蒂安。
  • 好的,所以对于 X=3 和 Y=2,您需要六个组。每个组应包含 G(可能值 1..X)和 P(可能值 1..Y)的可能组合。任何组合都是有效的。但是在一个组中,没有两个组合应该是相同的,并且必须至少有两个不同的 P 值?

标签: sql oracle algorithm plsql permutation


【解决方案1】:

我建议建立一种分支树。每次你添加一个“G”,实际上你在你的树中添加了一个关卡。

您想要的有序结果是到树叶的路线,除了外部路线(下图)。

              G1  G2  G3
        p1    ****************NO (p1 p1 p1)
       /
     p1
    /   \
   /     p2   (p1 p1 p2)
p1 
   \    p1    (p1 p2 p1)
    \  /
     p2 
      \ 
        p2    (p1 p2 p2)

        p1    (p2 p1 p1)
       /
     p1
    /   \
   /     p2   (p2 p1 p2)
p2       
   \    p1    (p2 p2 p1)
    \  /
     p2 
      \ 
        p2   **************NO (p2 p2 p1)

每次添加关卡时,复制每个 P 的元组,并将每个 P 添加到相同的位置。这是一种在这样的数组 (l_ttab) 中构建它的方法,您可以删除第一个和最后一个元素(+ 每个 G 的叉积)以满足您的需要。

类型

CREATE OR REPLACE TYPE t_list AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE TYPE tt_list AS TABLE OF t_list;

复制函数cpy_node:

CREATE OR REPLACE FUNCTION cpy_node(p_list in t_list)
  RETURN t_list
AS
  l_tab t_list := t_list();
BEGIN
  for i in 1..p_list.count loop
    l_tab.extend;
    l_tab(i):=p_list(i);
  end loop;
  RETURN l_tab;
END cpy_node;

从“P”函数cto_table的输入列表生成表格:

CREATE OR REPLACE FUNCTION cto_table(p_sep in Varchar2, p_list IN VARCHAR2)
  RETURN t_list
AS
  l_string VARCHAR2(32767) := p_list || p_sep;
  l_sep_index PLS_INTEGER;
  l_index PLS_INTEGER := 1;
  l_tab t_list := t_list();
BEGIN
  LOOP
    l_sep_index := INSTR(l_string, p_sep, l_index);
    EXIT
  WHEN l_sep_index = 0;
    l_tab.EXTEND;
    l_tab(l_tab.COUNT) := TRIM(SUBSTR(l_string,l_index,l_sep_index - l_index));
    l_index            := l_sep_index + 1;
  END LOOP;
  RETURN l_tab;
END cto_table;

创建*有序节点列表:

declare
  l_ttab tt_list :=tt_list();
  l_ttab_next tt_list :=tt_list();
  l_tab_p t_list;    
  l_tab t_list;    
  p_nb_lvl pls_integer:=2;
begin
  l_tab_p := cto_table(',', 'p1, p2');
  -- initiate table with single nodes
  for i in 1..l_tab_p.count loop
    l_ttab.extend;
    l_tab :=t_list();
    l_tab.extend;
    l_tab(l_tab.count):=l_tab_p(i);
    l_ttab(l_ttab.count):=l_tab;
  end loop;
  -- ( p1 ) ( p2 )

  for k in 1..p_nb_lvl-1 loop
    l_ttab_next := tt_list();
    for j in 1..l_ttab.count loop
      for i in 1..l_tab_p.count loop
        -- copy from current list
        l_ttab_next.extend;
        l_ttab_next(l_ttab_next.count):=cpy_node(l_ttab(j));
        -- add node at the end
        l_ttab_next(l_ttab_next.count).extend;
        l_ttab_next(l_ttab_next.count)(k+1):=l_tab_p(i);
      end loop;
    end loop;  
    l_ttab := l_ttab_next;
  end loop;    

  -- display result
  for i in 1..l_ttab.count loop
    for j in 1..l_ttab(i).count loop
        dbms_output.put(l_ttab(i)(j)||' ');
    end loop;
    dbms_output.put_line(chr(13)||'--');
  end loop;
  -- e.g. if p_nb_lvl=2:
  --(p1 p1) (p1 p2) (p2 p1) (p2 p2) (p1 p1) (p1 p2) ( p2 p1) ( p2 p2)

end;

*我并不是说上面是最佳的,因为我每次都重新复制树,但对我来说听起来更容易。

【讨论】:

  • 非常感谢。但是对于 t_list 和 tt_list,我需要先创建 varray 类型吗?可能是CREATE TYPE t_list AS VARRAY(10) OF Varchar2; 之类的东西?
  • 是的,没错,确实有 2 种类型。添加了CREATE OR REPLACE TYPE t_list AS TABLE OF VARCHAR2(100);CREATE OR REPLACE TYPE tt_list AS TABLE OF t_list;
  • @fadedbrandnew,它解决了你的问题吗?如果是这种情况,您可以接受此解决方案(它会给您代表点数)。如果没有,您可能会问一个可能有更多受众的新问题。
  • 非常感谢@J.Chomel,这是我正在进行的副项目之一,过去几天我一直忙于与其他紧急任务的 ddls 会面。我一定会调查并尽快回复您。再次感谢您的回答!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-20
  • 1970-01-01
  • 2020-12-06
  • 2016-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多