将过程参数传递给 SELECT INTO where 子句不起作用
Posted
技术标签:
【中文标题】将过程参数传递给 SELECT INTO where 子句不起作用【英文标题】:Passing procedure arguments to SELECT INTO where clause not working 【发布时间】:2015-07-17 15:43:02 【问题描述】:我有一个复合触发器,它使用 SELECT INTO 检查条件。如果满足条件,我会引发异常并且不会插入值。为此,我使用 NO_DATA_FOUND 异常,当一切正常时,这意味着不满足引发自定义异常的条件并且执行 INSERT。
困扰我的是,当我在 SELECT INTO where 子句中使用常量时,我正确地得到了 NO_DATA_FOUND 异常。当我使用与常量具有相同值的过程参数时,不会引发 NO_DATA_FOUND 异常。
这是触发器内部进行检查的过程:
PROCEDURE validate_system(
v_abgleich_id IN ctl_webadmin_abgleich.webadmin_abgleich_id%TYPE,
v_valid_from IN ctl_webadmin_abgleich.gueltig_von%TYPE,
v_valid_through IN ctl_webadmin_abgleich.gueltig_bis%TYPE,
v_source_system IN ctl_webadmin_abgleich.cldb_quellsystem_id%TYPE,
v_target_system IN ctl_webadmin_abgleich.cldb_zielsystem_id%TYPE,
v_table_id IN ctl_webadmin_abgleich.cldb_webadmin_table_id%TYPE)
IS
overlapping_abgleich EXCEPTION;
src_target_same EXCEPTION;
from_date_null EXCEPTION;
from_date_gt EXCEPTION;
dummy CHAR(1);
v_ss NUMBER(10,0);
BEGIN
v_ss := 4;
dbms_output.put_line('v_ss' || v_ss);
IF(validate_system.v_source_system = validate_system.v_target_system) THEN
dbms_output.put_line('Raising src_target_same.');
RAISE src_target_same;
END IF;
IF(validate_system.v_valid_from IS NULL) THEN
dbms_output.put_line('Raising from_date_null.');
RAISE from_date_null;
END IF;
IF(validate_system.v_valid_from > validate_system.v_valid_through) THEN
dbms_output.put_line('Raising from_date_gt.');
RAISE from_date_gt;
END IF;
BEGIN
SELECT 'X'
INTO dummy
FROM ctl_webadmin_abgleich ab
WHERE ab.cldb_quellsystem_id = 4 AND ROWNUM = 1; -- constant raises NO_DATA_FOUND
-- WHERE ab.cldb_quellsystem_id = validate_system.v_source_system AND ROWNUM = 1; -- variable with the same value doesn't raise NO_DATA_FOUND exception.
dbms_output.put_line('Conflicting recs foound: ' || v_ss);
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('NO DATA FOUND:' || validate_system.v_source_system);
NULL;
END;
EXCEPTION
WHEN src_target_same THEN
RAISE_APPLICATION_ERROR(-20001, 'Quellsystem darf nicht gleich dem Zielsystem sein!');
WHEN from_date_null THEN
RAISE_APPLICATION_ERROR(-20002, 'VON Datum darf nicht NULL sein!');
WHEN from_date_gt THEN
RAISE_APPLICATION_ERROR(-20003, 'VON Datum liegt vor Datum BIS!');
WHEN overlapping_abgleich THEN
RAISE_APPLICATION_ERROR(-20004, 'Gültigkeitsbereiche mit schon existierenden Einträgen kollidieren!');
END validate_system;
在一个空表中,如果我将一个常量传递给 SELECT INTO 的 WHERE 子句,我会得到 NO_DATA_FOUND。但是当我使用变量-> validate_system.v_source_system 时,不会引发异常。
我做错了什么?此过程从另一个过程调用,而另一个过程又在触发器的 AFTER STATEMENT 块中调用。
INSERT 如下:
Insert into CTL_WEBADMIN_ABGLEICH (webadmin_abgleich_id, gueltig_von, gueltig_bis, cldb_quellsystem_id, cldb_zielsystem_id, cldb_webadmin_table_id)
values (12, to_date('20.01.20', 'DD.MM.RR'), to_date('20.05.20', 'DD.MM.RR'), 4, 6, 813);
这是整个触发器:
create or replace
trigger abgleich_quellsystem_trg FOR INSERT OR
UPDATE ON ctl_webadmin_abgleich COMPOUND TRIGGER type abgleich_type IS record ( abgleich_id ctl_webadmin_abgleich.webadmin_abgleich_id%TYPE, valid_from ctl_webadmin_abgleich.gueltig_von%TYPE, valid_through ctl_webadmin_abgleich.gueltig_bis%TYPE, source_system ctl_webadmin_abgleich.cldb_quellsystem_id%TYPE, target_system ctl_webadmin_abgleich.cldb_zielsystem_id%TYPE, table_id ctl_webadmin_abgleich.cldb_webadmin_table_id%TYPE );
type abgleich_recs_type
IS
TABLE OF abgleich_type INDEX BY pls_integer;
abgleich_rec abgleich_recs_type;
PROCEDURE lock_system(
v_abgleich_id IN ctl_webadmin_abgleich.webadmin_abgleich_id%TYPE,
v_valid_from IN ctl_webadmin_abgleich.gueltig_von%TYPE,
v_valid_through IN ctl_webadmin_abgleich.gueltig_bis%TYPE,
v_source_system IN ctl_webadmin_abgleich.cldb_quellsystem_id%TYPE,
v_target_system IN ctl_webadmin_abgleich.cldb_zielsystem_id%TYPE,
v_table_id IN ctl_webadmin_abgleich.cldb_webadmin_table_id%TYPE)
IS
abgleich_id ctl_webadmin_abgleich.webadmin_abgleich_id%TYPE;
valid_from ctl_webadmin_abgleich.gueltig_von%TYPE;
valid_through ctl_webadmin_abgleich.gueltig_bis%TYPE;
source_system ctl_webadmin_abgleich.cldb_quellsystem_id%TYPE;
target_system ctl_webadmin_abgleich.cldb_zielsystem_id%TYPE;
table_id ctl_webadmin_abgleich.cldb_webadmin_table_id%TYPE;
BEGIN
dbms_output.put_line('Locking...' || lock_system.v_abgleich_id);
SELECT ab.webadmin_abgleich_id,
ab.gueltig_von,
ab.gueltig_bis,
ab.cldb_quellsystem_id,
ab.cldb_zielsystem_id,
ab.cldb_webadmin_table_id
INTO abgleich_id,
valid_from,
valid_through,
source_system,
target_system,
table_id
FROM ctl_webadmin_abgleich ab
WHERE ab.webadmin_abgleich_id = lock_system.v_abgleich_id FOR UPDATE;
END lock_system;
PROCEDURE validate_system(
v_abgleich_id IN ctl_webadmin_abgleich.webadmin_abgleich_id%TYPE,
v_valid_from IN ctl_webadmin_abgleich.gueltig_von%TYPE,
v_valid_through IN ctl_webadmin_abgleich.gueltig_bis%TYPE,
v_source_system IN ctl_webadmin_abgleich.cldb_quellsystem_id%TYPE,
v_target_system IN ctl_webadmin_abgleich.cldb_zielsystem_id%TYPE,
v_table_id IN ctl_webadmin_abgleich.cldb_webadmin_table_id%TYPE)
IS
overlapping_abgleich EXCEPTION;
src_target_same EXCEPTION;
from_date_null EXCEPTION;
from_date_gt EXCEPTION;
dummy CHAR(1);
BEGIN
IF(validate_system.v_source_system = validate_system.v_target_system) THEN
dbms_output.put_line('Raising src_target_same.');
RAISE src_target_same;
END IF;
IF(validate_system.v_valid_from IS NULL) THEN
dbms_output.put_line('Raising from_date_null.');
RAISE from_date_null;
END IF;
IF(validate_system.v_valid_from > validate_system.v_valid_through) THEN
dbms_output.put_line('Raising from_date_gt.');
RAISE from_date_gt;
END IF;
BEGIN
SELECT 'X'
INTO dummy
FROM ctl_webadmin_abgleich ab
WHERE ab.cldb_quellsystem_id = 5 AND ROWNUM = 1;
dbms_output.put_line('Conflicting recs foound: ' || validate_system.v_source_system);
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('NO DATA FOUND:' || validate_system.v_source_system);
NULL;
END;
EXCEPTION
WHEN src_target_same THEN
RAISE_APPLICATION_ERROR(-20001, 'Quellsystem darf nicht gleich dem Zielsystem sein!');
WHEN from_date_null THEN
RAISE_APPLICATION_ERROR(-20002, 'VON Datum darf nicht NULL sein!');
WHEN from_date_gt THEN
RAISE_APPLICATION_ERROR(-20003, 'VON Datum liegt vor Datum BIS!');
WHEN overlapping_abgleich THEN
RAISE_APPLICATION_ERROR(-20004, 'Gültigkeitsbereiche mit schon existierenden Einträgen kollidieren!');
END validate_system;
PROCEDURE validate_systems
IS
sys_idx pls_integer;
BEGIN
dbms_output.put_line('Start validation...');
sys_idx := abgleich_rec.first;
LOOP
EXIT
WHEN sys_idx IS NULL;
validate_system(abgleich_rec(sys_idx).abgleich_id, abgleich_rec(sys_idx).valid_from, abgleich_rec(sys_idx).valid_through, abgleich_rec(sys_idx).source_system, abgleich_rec(sys_idx).target_system, abgleich_rec(sys_idx).table_id);
sys_idx := abgleich_rec.next(sys_idx);
END LOOP;
END validate_systems;
BEFORE EACH ROW
IS
BEGIN
abgleich_rec(:NEW.webadmin_abgleich_id).abgleich_id := :NEW.webadmin_abgleich_id;
abgleich_rec(:NEW.webadmin_abgleich_id).valid_from := :NEW.gueltig_von;
abgleich_rec(:NEW.webadmin_abgleich_id).valid_through := :NEW.gueltig_bis;
abgleich_rec(:NEW.webadmin_abgleich_id).source_system := :NEW.cldb_quellsystem_id;
abgleich_rec(:NEW.webadmin_abgleich_id).target_system := :NEW.cldb_zielsystem_id;
abgleich_rec(:NEW.webadmin_abgleich_id).table_id := :NEW.cldb_webadmin_table_id;
END BEFORE EACH ROW;
AFTER STATEMENT
IS
BEGIN
dbms_output.ENABLE (buffer_size => NULL);
dbms_output.put_line('Start validation...');
validate_systems;
dbms_output.put_line('Validation successful.');
END AFTER STATEMENT;
END abgleich_quellsystem_trg;
还有表格的 DDL:
--------------------------------------------------------
-- DDL for Table CTL_WEBADMIN_ABGLEICH
--------------------------------------------------------
CREATE TABLE "CLDBDEF"."CTL_WEBADMIN_ABGLEICH"
( "WEBADMIN_ABGLEICH_ID" NUMBER(10,0),
"USERID_INS" NUMBER(10,0),
"TIMESTAMP_INS" TIMESTAMP (6),
"TIMESTAMP_UPD" TIMESTAMP (6),
"USERID_UPD" NUMBER(10,0),
"GUELTIG_VON" DATE,
"GUELTIG_BIS" DATE,
"CLDB_QUELLSYSTEM_ID" NUMBER(10,0),
"CLDB_WEBADMIN_TABLE_ID" NUMBER(10,0),
"CLDB_ZIELSYSTEM_ID" NUMBER(10,0)
);
--------------------------------------------------------
-- DDL for Index SYS_C0027983
--------------------------------------------------------
CREATE UNIQUE INDEX "CLDBDEF"."SYS_C0027983" ON "CLDBDEF"."CTL_WEBADMIN_ABGLEICH" ("WEBADMIN_ABGLEICH_ID") ;
--------------------------------------------------------
-- Constraints for Table CTL_WEBADMIN_ABGLEICH
--------------------------------------------------------
ALTER TABLE "CLDBDEF"."CTL_WEBADMIN_ABGLEICH" ADD PRIMARY KEY ("WEBADMIN_ABGLEICH_ID");
ALTER TABLE "CLDBDEF"."CTL_WEBADMIN_ABGLEICH" MODIFY ("WEBADMIN_ABGLEICH_ID" NOT NULL ENABLE);
感谢大家的帮助! 人
更新:好的,我已经添加了
select count(*) into recnr from ctl_webadmin_abgleich ab;
dbms_output.put_line('Rec nr: ' || recnr);
在 SELECT INTO 之后,显然 INSERT 被执行并且 count(*) 返回 1。这怎么可能?有人可以解释一下吗?谢谢!
【问题讨论】:
顺便说一句,为什么在插入语句中使用 2 位数年份?我知道千年虫是很久以前的事了,但肯定不是很久以前吧?为什么要依靠数据库来猜测您的意思是什么年份,什么时候您可以轻松地传递确切的年份? 客户要求。他们有一个巨大的数据库,里面有很多表格,所有的日期都有这种格式。 太糟糕了! *:-( 你会认为在这个时代 4 位数的年份将是标准!他们将日期存储在字符串或数字中?Ick! 至于你的问题;您正在 after 语句部分中进行验证,此时该行已经存在。因此,当您使用作为 validate_system 过程的一部分传入的参数进行选择时,您不会收到 no_data_found 错误。嗯,虽然没有解释为什么你会在常量版本上得到错误。 啊,在您的最终触发器中,常量为 5,但您插入的是 4。我希望在这种情况下看到错误。如果常量设置为 4,我不希望看到错误。 【参考方案1】:正如 Boneist 注意到,当您检查冲突时,您尝试插入的记录已经存在于表中。更改您的选择语句以排除您正在插入的记录的记录 ID,它应该按预期运行:
SELECT 'X'
INTO dummy
FROM ctl_webadmin_abgleich ab
WHERE ab.cldb_quellsystem_id = v_source_system
AND ab.WEBADMIN_ABGLEICH_ID != v_abgleich_id -- Add this condition
AND ROWNUM = 1;
【讨论】:
以上是关于将过程参数传递给 SELECT INTO where 子句不起作用的主要内容,如果未能解决你的问题,请参考以下文章
将'select from dual'作为参数传递给java可调用语句
将参数传递给 jQuery 的 select2 ajax 调用
将 SELECT 结果作为参数传递给 postgreSQL 函数
将附加参数传递给 React Ant Design Select 组件的 onSelect 方法