在存储过程中违反了唯一约束。

huangapple go评论72阅读模式
英文:

unique constraint violated in stored procedure oracle

问题

我有以下存储过程:

create or replace PROCEDURE CALCULATE_RECOVERY_HISTORY(p_month IN VARCHAR2) AS 
l_id NUMBER; 
BEGIN
  ADD_LOG_INFO('CALCULATE_RECOVERY_HISTORY', 'Procedure Started');
  
  l_id := SQ_AP_RECOVERY_HISTORY.NEXTVAL;

    INSERT INTO t_ap_recovery_history (ID, RECOVERY_TARGET_MONTH, TARGET_INSTANCE, RECOVERY_PROGRESS, RECOVERY_TARGET, FAILED_TO_RECOVERY, FOCUS_AREA, IDENTIFIER_CLASS, CREATED_ON) 
    SELECT  l_id,
            a_recovery_target_month, 
            a_target_instance, 
            COUNT(CASE WHEN A_IS_RECOVERED = 'Y' THEN 1 END), 
            COUNT(CASE WHEN A_IS_RECOVERED IN ('Y', 'N') THEN 1 END), 
            COUNT(CASE WHEN A_IS_RECOVERED = 'N' THEN 1 END),
            f.focus_area,
            r.identifier_class,
            SYSDATE
            from t_ap_recovery_target t, t_ap_recovery_focusarea f, range r
    where t.a_focus_area_id = f.id and t.a_range_id = r.id
    and t.a_recovery_target_month = p_month
    group by a_target_instance, a_recovery_target_month, f.focus_area, r.identifier_class;
    
      COMMIT;
END CALCULATE_RECOVERY_HISTORY;

当我运行该存储过程时,我收到以下错误信息:

ORA-00001: 违反唯一约束

我还尝试了另一种方式:

SELECT  SQ_AP_RECOVERY_HISTORY.NEXTVAL, a_recovery_target_month ... 

但这也返回了另一个错误:

不允许在此处使用序列号

为了解决此约束问题,您可以尝试以下更改代码:

  1. 在存储过程中,确保SQ_AP_RECOVERY_HISTORY.NEXTVAL的值在插入语句之前获取,例如:
l_id := SQ_AP_RECOVERY_HISTORY.NEXTVAL;

然后在INSERT语句中使用l_id而不是SQ_AP_RECOVERY_HISTORY.NEXTVAL。

  1. 确保INSERT语句中的数据不会违反T_AP_RECOVERY_HISTORY表的唯一约束。检查是否有重复的ID值尝试插入。

这些更改应该有助于解决唯一约束问题。希望对您有所帮助。

英文:

I have below stored procedure:

create or replace PROCEDURE CALCULATE_RECOVERY_HISTORY(p_month IN VARCHAR2) AS 
l_id NUMBER; 
BEGIN
  ADD_LOG_INFO('CALCULATE_RECOVERY_HISTORY', 'Procedure Started');
  
  l_id := SQ_AP_RECOVERY_HISTORY.NEXTVAL;

    INSERT INTO t_ap_recovery_history (ID, RECOVERY_TARGET_MONTH, TARGET_INSTANCE, RECOVERY_PROGRESS, RECOVERY_TARGET, FAILED_TO_RECOVERY, FOCUS_AREA, IDENTIFIER_CLASS, CREATED_ON) 
    SELECT  l_id,
            a_recovery_target_month, 
            a_target_instance, 
            COUNT(CASE WHEN A_IS_RECOVERED = 'Y' THEN 1 END), 
            COUNT(CASE WHEN A_IS_RECOVERED IN ('Y', 'N') THEN 1 END), 
            COUNT(CASE WHEN A_IS_RECOVERED = 'N' THEN 1 END),
            f.focus_area,
            r.identifier_class,
            SYSDATE
            from t_ap_recovery_target t, t_ap_recovery_focusarea f, range r
    where t.a_focus_area_id = f.id and t.a_range_id = r.id
    and t.a_recovery_target_month = p_month
    group by a_target_instance, a_recovery_target_month, f.focus_area, r.identifier_class;
    
      COMMIT;
END CALCULATE_RECOVERY_HISTORY;

When I run the procedure, I get the error

> ORA-00001: unique constraint violated.

I've also tried another way which is

SELECT  SQ_AP_RECOVERY_HISTORY.NEXTVAL, a_recovery_target_month ... 

But this also return another error which is

> Sequence number not allowed here

What should I change in the code to solve this constraint issue?

Below is the table definition for T_AP_RECOVERY_HISTORY

在存储过程中违反了唯一约束。

  CREATE TABLE "DIMSPST"."T_AP_RECOVERY_HISTORY" 
   (	"ID" NUMBER(38,0), 
	"RECOVERY_TARGET_MONTH" VARCHAR2(6 BYTE) DEFAULT TO_CHAR(SYSTIMESTAMP, 'YYYYMM'), 
	"TARGET_INSTANCE" VARCHAR2(20 BYTE), 
	"RECOVERY_PROGRESS" NUMBER(38,0), 
	"RECOVERY_TARGET" NUMBER(38,0), 
	"FAILED_TO_RECOVERY" NUMBER(38,0), 
	"FOCUS_AREA" VARCHAR2(20 BYTE), 
	"IDENTIFIER_CLASS" VARCHAR2(42 BYTE), 
	"CREATED_ON" TIMESTAMP (6), 
	 PRIMARY KEY ("ID")

答案1

得分: 3

在子查询中执行聚合操作,然后在外部查询中应用序列值:

CREATE PROCEDURE CALCULATE_RECOVERY_HISTORY(
  p_month IN VARCHAR2
)
AS 
BEGIN
  ADD_LOG_INFO('CALCULATE_RECOVERY_HISTORY', 'Procedure Started');

  INSERT INTO t_ap_recovery_history (
    ID,
    RECOVERY_TARGET_MONTH,
    TARGET_INSTANCE,
    RECOVERY_PROGRESS,
    RECOVERY_TARGET,
    FAILED_TO_RECOVERY,
    FOCUS_AREA,
    IDENTIFIER_CLASS,
    CREATED_ON
  )
  SELECT SQ_AP_RECOVERY_HISTORY.NEXTVAL,
         a_recovery_target_month, 
         a_target_instance, 
         RECOVERY_PROGRESS,
         RECOVERY_TARGET, 
         FAILED_TO_RECOVERY,
         focus_area,
         identifier_class,
         SYSDATE
  FROM   (
    SELECT a_recovery_target_month, 
           a_target_instance, 
           COUNT(CASE WHEN A_IS_RECOVERED = 'Y' THEN 1 END) AS RECOVERY_PROGRESS,
           COUNT(CASE WHEN A_IS_RECOVERED IN ('Y', 'N') THEN 1 END) AS RECOVERY_TARGET, 
           COUNT(CASE WHEN A_IS_RECOVERED = 'N' THEN 1 END) AS FAILED_TO_RECOVERY,
           f.focus_area,
           r.identifier_class
    FROM   t_ap_recovery_target t
           INNER JOIN t_ap_recovery_focusarea f
           ON (t.a_focus_area_id = f.id)
           INNER JOIN range r
           ON (t.a_range_id = r.id)
    WHERE  t.a_recovery_target_month = p_month
    GROUP BY
           a_target_instance, 
           a_recovery_target_month,
           f.focus_area,
           r.identifier_class
  );
END CALCULATE_RECOVERY_HISTORY;
/

注意:如果在存储过程中使用COMMIT,则无法将多个存储过程链接在一起,如果其中一个失败,则会回滚所有操作。相反,应该在调用存储过程的代码块中执行COMMIT

fiddle

英文:

Perform the aggregation in a sub-query and then apply the sequence value in an outer-query:

CREATE PROCEDURE CALCULATE_RECOVERY_HISTORY(
  p_month IN VARCHAR2
)
AS 
BEGIN
  ADD_LOG_INFO('CALCULATE_RECOVERY_HISTORY', 'Procedure Started');

  INSERT INTO t_ap_recovery_history (
    ID,
    RECOVERY_TARGET_MONTH,
    TARGET_INSTANCE,
    RECOVERY_PROGRESS,
    RECOVERY_TARGET,
    FAILED_TO_RECOVERY,
    FOCUS_AREA,
    IDENTIFIER_CLASS,
    CREATED_ON
  )
  SELECT SQ_AP_RECOVERY_HISTORY.NEXTVAL,
         a_recovery_target_month, 
         a_target_instance, 
         RECOVERY_PROGRESS,
         RECOVERY_TARGET, 
         FAILED_TO_RECOVERY,
         focus_area,
         identifier_class,
         SYSDATE
  FROM   (
    SELECT a_recovery_target_month, 
           a_target_instance, 
           COUNT(CASE WHEN A_IS_RECOVERED = 'Y' THEN 1 END) AS RECOVERY_PROGRESS,
           COUNT(CASE WHEN A_IS_RECOVERED IN ('Y', 'N') THEN 1 END) AS RECOVERY_TARGET, 
           COUNT(CASE WHEN A_IS_RECOVERED = 'N' THEN 1 END) AS FAILED_TO_RECOVERY,
           f.focus_area,
           r.identifier_class
    FROM   t_ap_recovery_target t
           INNER JOIN t_ap_recovery_focusarea f
           ON (t.a_focus_area_id = f.id)
           INNER JOIN range r
           ON (t.a_range_id = r.id)
    WHERE  t.a_recovery_target_month = p_month
    GROUP BY
           a_target_instance, 
           a_recovery_target_month,
           f.focus_area,
           r.identifier_class
  );
END CALCULATE_RECOVERY_HISTORY;
/

Note: If you COMMIT in stored procedures then you cannot chain multiple procedures together and if one fails then ROLLBACK then all. Instead, you should COMMIT in the block that you call the procedures from.

fiddle

答案2

得分: 1

一种选择是让 Oracle 创建 ID。您没有指定您使用的数据库版本,所以触发器肯定是有效的解决方法:

create or replace trigger trg_bi_rec_hist
  before insert on t_ap_recovery_history
  for each row
begin
  :new.id := SQ_AP_RECOVERY_HISTORY.NEXTVAL;
end;
/

然后过程中不需要插入到 ID 列,即:

INSERT INTO t_ap_recovery_history (RECOVERY_TARGET_MONTH, ...)
SELECT a_recovery_target_month, ...

另一种选择(如果您的数据库版本支持)是创建 ID 作为自动生成的标识列,而不是触发器,例如:

create table test
  (id number generated always as identity);

这些是代码部分的翻译。

英文:

One option is to let Oracle create ID. You didn't specify database version you use, so trigger certainly is what would work:

create or replace trigger trg_bi_rec_hist
  before insert on t_ap_recovery_history
  for each row
begin
  :new.id := SQ_AP_RECOVERY_HISTORY.NEXTVAL;
end;
/

Procedure then wouldn't contain insert into the ID column, i.e.

INSERT INTO t_ap_recovery_history (RECOVERY_TARGET_MONTH, ...)
SELECT a_recovery_target_month, ...

Another option (if your database version supports it) is to create ID as identity column instead of a trigger, e.g.

SQL> create table test
  2    (id number generated always as identity);

Table created.

答案3

得分: 1

或者,如果您不想像前面的答案中创建触发器,那么该存储过程应该如下所示:

create or replace PROCEDURE CALCULATE_RECOVERY_HISTORY(p_month IN VARCHAR2) AS 
l_id NUMBER; 
BEGIN
  ADD_LOG_INFO('CALCULATE_RECOVERY_HISTORY', 'Procedure Started');
  
  INSERT INTO t_ap_recovery_history (ID, RECOVERY_TARGET_MONTH, 
    TARGET_INSTANCE, RECOVERY_PROGRESS, RECOVERY_TARGET,
    FAILED_TO_RECOVERY, FOCUS_AREA, IDENTIFIER_CLASS, CREATED_ON)
  with tb as ( 
  SELECT  a_recovery_target_month, 
       a_target_instance, 
       COUNT(CASE WHEN A_IS_RECOVERED = 'Y' THEN 1 END) c1, 
       COUNT(CASE WHEN A_IS_RECOVERED IN ('Y', 'N') THEN 1 END) c2, 
       COUNT(CASE WHEN A_IS_RECOVERED = 'N' THEN 1 END) c3,
       f.focus_area,
       r.identifier_class
  from t_ap_recovery_target t, t_ap_recovery_focusarea f, range r
  where t.a_focus_area_id = f.id and t.a_range_id = r.id
      and t.a_recovery_target_month = p_month
  group by a_target_instance, a_recovery_target_month, 
    f.focus_area, r.identifier_class
  )
  select SQ_AP_RECOVERY_HISTORY.NEXTVAL,
       a_recovery_target_month, 
       a_target_instance, 
       c1, 
       c2, 
       c3,
       focus_area,
       identifier_class,
       sysdate 
  from tb;

  COMMIT;
END CALCULATE_RECOVERY_HISTORY;

一般来说,如果您一次性插入大量行,进行大规模更新、大规模删除或合并操作,触发器对性能有害。如果只有影响少量行的DML操作,触发器可能会减少复杂性,尽管我更倾向于在存储过程中执行更多操作,减少触发器中的操作。

英文:

Or, if you would not create the trigger like in the previous answer, the procedure should look like below:

create or replace PROCEDURE CALCULATE_RECOVERY_HISTORY(p_month IN VARCHAR2) AS 
l_id NUMBER; 
BEGIN
  ADD_LOG_INFO('CALCULATE_RECOVERY_HISTORY', 'Procedure Started');
  

      INSERT INTO t_ap_recovery_history (ID, RECOVERY_TARGET_MONTH, 
        TARGET_INSTANCE, RECOVERY_PROGRESS, RECOVERY_TARGET,
        FAILED_TO_RECOVERY, FOCUS_AREA, IDENTIFIER_CLASS, CREATED_ON)
      with tb as ( 
      SELECT  a_recovery_target_month, 
           a_target_instance, 
           COUNT(CASE WHEN A_IS_RECOVERED = 'Y' THEN 1 END) c1, 
           COUNT(CASE WHEN A_IS_RECOVERED IN ('Y', 'N') THEN 1 END) c2, 
           COUNT(CASE WHEN A_IS_RECOVERED = 'N' THEN 1 END) c3,
           f.focus_area,
           r.identifier_class
      from t_ap_recovery_target t, t_ap_recovery_focusarea f, range r
      where t.a_focus_area_id = f.id and t.a_range_id = r.id
          and t.a_recovery_target_month = p_month
      group by a_target_instance, a_recovery_target_month, 
        f.focus_area, r.identifier_class
      )
      select SQ_AP_RECOVERY_HISTORY.NEXTVAL,
           a_recovery_target_month, 
           a_target_instance, 
           c1, 
           c2, 
           c3,
           focus_area,
           identifier_class,
           sysdate 
      from tb;
    
      COMMIT;
END CALCULATE_RECOVERY_HISTORY;

In general triggers are detrimental to performance in case you have insert select inserting large numbers of rows in one go, or massive updates or massive deletes or merge.
If you have only DML affecting a small number of rows, triggers may save complexity, although I'd rather do more in stored procedures and less in triggers.

huangapple
  • 本文由 发表于 2023年2月8日 16:26:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/75383025.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定