英文:
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 ...
但这也返回了另一个错误:
不允许在此处使用序列号
为了解决此约束问题,您可以尝试以下更改代码:
- 在存储过程中,确保SQ_AP_RECOVERY_HISTORY.NEXTVAL的值在插入语句之前获取,例如:
l_id := SQ_AP_RECOVERY_HISTORY.NEXTVAL;
然后在INSERT语句中使用l_id而不是SQ_AP_RECOVERY_HISTORY.NEXTVAL。
- 确保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
。
英文:
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.
答案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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论