1

我正在尝试执行代码重写。其中之一就是这个怪物。我有一组普通的 DML,突然间我在一个脚本中有这个 PL/SQL 块(请参阅当前的解决方案),这在 SQL DML 中看起来很奇怪。

最初我们决定使用 PL/SQL 块,假设是,"updating a column with a sequence number without increment within the group of records and incremented for the next group CANNOT BE achieved in a single SQL."

row1 - seq.nextval
row2 - seq.currval
row3 - seq.nextval
row4 - seq.currval
row5 - seq.currval

where group1 {row1, row2} and group 2 {row3, row4, row5}

问题:如何在一组行之后或有条件地增加一个 oracle 序列?

数据设置:

CREATE TABLE TEMP_GP_SEQ
(
   COL1   NUMBER,
   COL2   NUMBER,
   COL3   NUMBER,
   COL4   NUMBER,
   COL5   NUMBER,
   COL6   NUMBER,
   COL7   VARCHAR2 (10)
);

INSERT INTO TEMP_GP_SEQ VALUES(1,10,100,NULL,NULL,NULL,NULL);
INSERT INTO TEMP_GP_SEQ VALUES(1,10,101,NULL,NULL,NULL,NULL);
INSERT INTO TEMP_GP_SEQ VALUES(12,10,100,1,NULL,NULL,NULL);
INSERT INTO TEMP_GP_SEQ VALUES(1,10,100,2,NULL,NULL,NULL);
INSERT INTO TEMP_GP_SEQ VALUES(1,10,100,3,NULL,NULL,NULL);
INSERT INTO TEMP_GP_SEQ VALUES(12,10,100,1,NULL,NULL,NULL);
INSERT INTO TEMP_GP_SEQ VALUES(2,10,100,NULL,NULL,NULL,NULL);
INSERT INTO TEMP_GP_SEQ VALUES(2,10,101,NULL,NULL,NULL,NULL);
INSERT INTO TEMP_GP_SEQ VALUES(2,10,101,1,NULL,NULL,NULL);
INSERT INTO TEMP_GP_SEQ VALUES(2,10,101,1,NULL,NULL,NULL);

COMMIT; 

CREATE SEQUENCE SEQ_TEMP_TEST
   START WITH 1
   INCREMENT BY 1;

所以我有关记录的选择标准

SELECT
      COL1,
      COL3,
      COL4,
      COUNT ( * )
FROM
      TEMP_GP_SEQ
GROUP BY
      COL1,
      COL3,
      COL4
HAVING
      COUNT ( * ) > 1;

会给我

COL1    COL3    COL4    COUNT(*)
2       101     1       2
12      100     1       2

我需要根据上面提到的分组范围使用创建的序列更新 TEMP_GP_SEQ 表和 COLUMN COL7 表。但是序列不应该为每条记录增加,它应该只随着组的变化而增加。例如

SELECT 
      COL1,
      COL2,
      COL3,
      COL4,
      COL7
FROM
      TEMP_GP_SEQ;

期望的输出

COL1    COL2    COL3   COL4    COL7         
1       10      100    NULL    NULL         
1       10      101    NULL    NULL         
12      10      100    1       M2           
1       10      100    2       NULL         
1       10      100    3       NULL         
12      10      100    1       M2           
2       10      100    NULL    NULL         
2       10      101    NULL    NULL         
2       10      101    1       M1           
2       10      101    1       M1           

COL7 为这四行更新,以 M 作为前缀词,M 之后的数字来自序列。仅当分组标准发生变化并且在整个组中保持不变时,数字才会发生变化(序列增加)。

挑战是任何列中都可能存在 NULL 值。因此,在分组 NULLS 时应该考虑。因此使用 IS NULL。(为了确定,NVL 被忽略了)

目前的解决方案:

DECLARE
    VAL INTEGER;
BEGIN
    FOR REC IN ( SELECT
                    COL1,
                    COL3,
                    COL4
              FROM
                    TEMP_GP_SEQ
              GROUP BY
                    COL1,
                    COL3,
                    COL4
              HAVING
                    COUNT ( * ) > 1 )
    LOOP
        SELECT SEQ_TEMP_TEST.NEXTVAL INTO VAL FROM DUAL;

        UPDATE
              TEMP_GP_SEQ
        SET
              COL7   = 'M' || VAL
        WHERE
                 ( COL1 = REC.COL1 OR ( COL1 IS NULL AND REC.COL1 IS NULL ) )
              AND ( COL3 = REC.COL3 OR ( COL3 IS NULL AND REC.COL3 IS NULL ) )
              AND ( COL4 = REC.COL4 OR ( COL4 IS NULL AND REC.COL4 IS NULL ) );
    END LOOP;
END;
/

真的可以将其重构为普通 SQL 而不是 PL/SQL 块吗?如果您需要任何澄清,请告诉我。

任何想尝试的人都可以在这里看到小提琴

4

1 回答 1

2

如果我理解你的问题,这个查询会做你想做的事,那么你只需要用它来更新你的行。我没有使用update更改行也没有使用小提琴,因为(我不知道为什么)它不会让我创建一个函数。

为了让我的 select 语句运行,我需要创建一个函数来返回序列,因为 oracle 不允许我在我的 sql 语句中使用它(至少不是在我的版本 10.2.x 上)。

所以首先我创建了这个函数:

create or replace function retSeq return number
as
   n number;
begin
   select SEQ_TEMP_TEST.nextval into n from dual;
   return n;
end;

然后我做了选择语句。我认为将您的代码更改为此会变得更加难以理解。但问题是用一个查询来解决问题,我几乎做到了(必须创建函数)。所以,不要害怕:

SELECT s1.col1, s1.col2, s1.col3, s1.col4, s1.col5, s1.col6, 
       decode(s1.id,null,'','M')
           || (SELECT retseq seq
                 FROM (SELECT   col1, col3, col4,
                                ROW_NUMBER () OVER (ORDER BY col1, col3, col4) ID
                         FROM temp_gp_seq
                        GROUP BY col1, col3, col4
                       HAVING COUNT (*) > 1)
                WHERE ID = s1.ID)
           as col7 
  FROM (SELECT a.*, b.ID
          FROM temp_gp_seq a,
               (SELECT col1, col3, col4,
                       ROW_NUMBER () OVER (ORDER BY col1, col3, col4) ID
                  FROM (SELECT   col1, col3, col4, COUNT (*) ct
                            FROM temp_gp_seq
                        GROUP BY col1, col3, col4
                          HAVING COUNT (*) > 1)) b
         WHERE a.col1 = b.col1(+) 
           AND a.col3 = b.col3(+)
           AND a.col4 = b.col4(+)) s1

结果将是(在第一次运行时,由于顺序)

COL1     COL2     COL3     COL4     COL5     COL6     COL7
 2        10       101      1                          M1
 2        10       101      1                          M1
 12       10       100      1                          M2
 12       10       100      1                          M2
 1        10       100      3                          
 1        10       100      2                          
 1        10       100                                
 2        10       101                                
 1        10       101                                
 2        10       100                                

由 OP 跟进:

SELECT
      A.COL1,
      A.COL2,
      A.COL3,
      A.COL4,
      A.COL5,
      A.COL6,
      DECODE ( B.ID, NULL, '', 'M' )
      || SEQ
          AS COL7
FROM
      TEMP_GP_SEQ A,
      (SELECT
            COL1,
            COL3,
            COL4,
            RETSEQ SEQ,
            ROW_NUMBER ( )
                OVER ( ORDER BY
                          COL1,
                          COL3,
                          COL4 )
                ID
       FROM
            TEMP_GP_SEQ
       GROUP BY
            COL1,
            COL3,
            COL4
       HAVING
            COUNT ( * ) > 1) B
WHERE
         A.COL1 = B.COL1(+)
      AND A.COL3 = B.COL3(+)
      AND A.COL4 = B.COL4(+);

PS:删除了不必要的子查询并将窗口功能合二为一。

于 2013-11-06T18:59:47.010 回答