将过程参数传递给 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 方法

Scala / Spark:如何将此参数传递给.select语句

mysql 存储过程 数据库表名字段作为参数传递给存储过程的方法