如何在一组行之后或在没有PL/SQL块的情况下有条件地递增oracle序列?
我正在尝试执行代码重写。其中之一就是这个怪物。我有一组普通的DML,突然在脚本中出现了这个PL/SQL块(请参阅当前的解决方案),这在SQL DML中看起来很奇怪 最初,我们决定使用PL/SQL块,其假设是,如何在一组行之后或在没有PL/SQL块的情况下有条件地递增oracle序列?,sql,database,oracle,plsql,oracle11g,Sql,Database,Oracle,Plsql,Oracle11g,我正在尝试执行代码重写。其中之一就是这个怪物。我有一组普通的DML,突然在脚本中出现了这个PL/SQL块(请参阅当前的解决方案),这在SQL DML中看起来很奇怪 最初,我们决定使用PL/SQL块,其假设是,“在记录组中使用序列号更新列而不使用增量,在下一组中使用增量是无法在单个SQL中实现的。” 问题:如何在一组行之后或有条件地递增oracle序列 数据设置: CREATE TABLE TEMP_GP_SEQ ( COL1 NUMBER, COL2 NUMBER, C
“在记录组中使用序列号更新列而不使用增量,在下一组中使用增量是无法在单个SQL中实现的。”
问题:如何在一组行之后或有条件地递增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;
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;
/
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(+);
因此,我的相关记录的选择标准
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
我需要根据上面提到的分组范围,使用创建的序列更新临时表和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。(为了确保安全,NVL被忽略)
现有解决方案:
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;
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;
/
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(+);
真的有可能将其重构为普通SQL而不是PL/SQL块吗?
如果您需要任何澄清,请告诉我
任何想尝试的人都可以看到小提琴如果我正确理解您的问题,此查询将满足您的要求,那么您只需要使用它来更新您的行但是我没有使用
更新
来更改行,也没有使用fiddle,因为(我不知道为什么)它不允许我创建函数
为了运行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语句。我认为把代码改成这个会使它更难理解。但问题是要用一个查询解决问题,我几乎做到了(必须创建函数)。所以,不要害怕:
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
结果将是(在第一次运行时,由于顺序原因)
由OP跟进:
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;
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;
/
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:删除了不必要的子查询,并将窗口函数组合在一个查询中。我相信任何pl/sql都可以转换为sql。我经常使用NVL来避免空值,也许这会有所帮助?我也相信这一点,因此发布了以下内容:)。只有当我
1000%确定虚拟值永远不会出现在列上时,才能使用NVL
。为了避免这种情况,我使用了IS NULL
。ROWNUM在oracle中也是一个有用的转换结构。仍然在看你的SQL代码,我的眼睛都睁大了。这个序列是必要的吗?或者你可以考虑不使用它?绝对必要。这正是我所做的,我检查并简化了你的查询,但逻辑上完全正确。:)见下文up@realspirituals是的,那太好了,我是一步一步地解决问题的,我没有尽可能地去做。感谢您的跟进:)