如何在触发器中实现我的功能?

时间:2019-10-30 15:12:07

标签: sql oracle function plsql

大家好,我在PLSQL中使用iban检查器来检查用户是否填写了正确的IBAN。

我已经在Internet上查找了该函数,现在我需要插入一个触发器,但是我尝试了它却给出了错误

当用户在APEX应用程序上输入iban nummer且ibannummers错误时,触发器需要使其起作用

这是我的功能:

create or replace FUNCTION CheckIBANNUMMBER(
      pIBAN IN VARCHAR2
    ) RETURN VARCHAR IS
      lResult     INTEGER;
      ResulText   Varchar(250);
      IBAN        VARCHAR2(256);
      IBAN_Digits VARCHAR2(256);
      l_mod       NUMBER;
      lTmp        VARCHAR2(8);
      lSCnt       INTEGER := 5;
      i           INTEGER := 1;

---

      FUNCTION fn_GetIBANDigits RETURN VARCHAR2 AS
        lChar   VARCHAR2(1);
        lNumber INTEGER;
        lString VARCHAR2(255);
      BEGIN
        FOR i IN 1..LENGTH(IBAN) LOOP
          lChar := SUBSTR(IBAN, i, 1);
          BEGIN
            lNumber := ASCII(lChar);
            IF lNumber > 47 AND lNumber < 58 THEN
              -- It's number 0 ... 9
              lString := lString || TO_CHAR(lNumber - 48);
            ELSE
              lString := lString || TO_CHAR(lNumber - 55);
            END IF;
          END;
        END LOOP;
        RETURN lString;
      END fn_GetIBANDigits;

---

     BEGIN
      IBAN := SUBSTR(pIBAN, 5) || SUBSTR(pIBAN, 1, 4);

      IBAN_Digits := fn_GetIBANDigits;

      LOOP
        lTmp := SUBSTR(IBAN_Digits, i, lSCnt);
        EXIT WHEN lTmp IS NULL;

        IF l_mod IS NULL THEN
          l_mod := MOD( TO_NUMBER(lTmp), 97);
        ELSE
          l_mod := MOD(TO_NUMBER( TO_CHAR(l_mod) || lTmp), 97);
        END IF;

        i := i + lSCnt;
      END LOOP;

      IF l_mod = 1 THEN
        lResult := 1;
        ResulText := 'The given IBAN is correct';
      ELSE
        lResult := 0;
        ResulText := 'The given IBAN is incorrect';
      END IF;

      RETURN(ResulText);
    END CheckIBANNUMMBER;

这是我做的触发器

create or replace trigger "T_CHECKIBAN"
BEFORE
insert or update on "PAYMENTS"
for each row
begin
if CHECKIBAN(new.IBAN) = 0 then raise_application_error(-20500,'Wrong IBANNUMMBER');
end;

2 个答案:

答案 0 :(得分:2)

使用虚拟列和CHECK约束,然后您的有效性检查将在表的DDL语句中进行,而不是在另一个城堡(即触发器)中进行:

create or replace FUNCTION CheckIBANNumber(
  pIBAN IN VARCHAR2
) RETURN NUMBER DETERMINISTIC
IS
  lResult     INTEGER;
  ResulText   Varchar(250);
  IBAN        VARCHAR2(256);
  IBAN_Digits VARCHAR2(256);
  l_mod       NUMBER;
  lTmp        VARCHAR2(8);
  lSCnt       INTEGER := 5;
  i           INTEGER := 1;

---

  FUNCTION fn_GetIBANDigits RETURN VARCHAR2 AS
    lChar   VARCHAR2(1);
    lNumber INTEGER;
    lString VARCHAR2(255);
  BEGIN
    FOR i IN 1..LENGTH(IBAN) LOOP
      lChar := SUBSTR(IBAN, i, 1);
      BEGIN
        lNumber := ASCII(lChar);
        IF lChar BETWEEN '0' AND '9' THEN
          -- It's number 0 ... 9
          lString := lString || lChar;
        ELSIF lChar BETWEEN 'A' AND 'Z' THEN 
          lString := lString || TO_CHAR(ASCII(lChar) - 55);
        END IF;
      END;
    END LOOP;
    RETURN lString;
  END fn_GetIBANDigits;

---

BEGIN
  IBAN := SUBSTR(pIBAN, 5) || SUBSTR(pIBAN, 1, 4);

  IBAN_Digits := fn_GetIBANDigits;

  LOOP
    lTmp := SUBSTR(IBAN_Digits, i, lSCnt);
    EXIT WHEN lTmp IS NULL;

    IF l_mod IS NULL THEN
      l_mod := MOD( TO_NUMBER(lTmp), 97);
    ELSE
      l_mod := MOD(TO_NUMBER( TO_CHAR(l_mod) || lTmp), 97);
    END IF;

    i := i + lSCnt;
  END LOOP;

  IF l_mod = 1 THEN
    RETURN 1;
  ELSE
    RETURN 0;
  END IF;
END CheckIBANNumber;
/
CREATE TABLE test_data (
  Country      VARCHAR2(20),
  IBAN         VARCHAR2(50),
  IBANValidity NUMBER(1) GENERATED ALWAYS AS ( CheckIBANNumber( iban ) ),
  CONSTRAINT IsValidIBAN CHECK ( IBANValidity = 1 )
);

然后(使用维基百科的测试数据):

INSERT INTO test_data ( Country, IBAN ) VALUES ( 'Belgium', 'BE71 0961 2345 6769' );
INSERT INTO test_data ( Country, IBAN ) VALUES ( 'France', 'FR76 3000 6000 0112 3456 7890 189' );
INSERT INTO test_data ( Country, IBAN ) VALUES ( 'Germany', 'DE91 1000 0000 0123 4567 89' );
INSERT INTO test_data ( Country, IBAN ) VALUES ( 'Greece', 'GR96 0810 0010 0000 0123 4567 890' );
INSERT INTO test_data ( Country, IBAN ) VALUES ( 'Romania', 'RO09 BCYP 0000 0012 3456 7890' );
INSERT INTO test_data ( Country, IBAN ) VALUES ( 'Saudi Arabia', 'SA44 2000 0001 2345 6789 1234' );
INSERT INTO test_data ( Country, IBAN ) VALUES ( 'Spain', 'ES79 2100 0813 6101 2345 6789' );
INSERT INTO test_data ( Country, IBAN ) VALUES ( 'Switzerland', 'CH56 0483 5012 3456 7800 9' );
INSERT INTO test_data ( Country, IBAN ) VALUES ( 'United Kingdom', 'GB98 MIDL 0700 9312 3456 78' );

成功,并且:

 SELECT * FROM test_data;

输出:

COUNTRY        | IBAN                              | IBANVALIDITY
:------------- | :-------------------------------- | -----------:
Belgium        | BE71 0961 2345 6769               |            1
France         | FR76 3000 6000 0112 3456 7890 189 |            1
Germany        | DE91 1000 0000 0123 4567 89       |            1
Greece         | GR96 0810 0010 0000 0123 4567 890 |            1
Romania        | RO09 BCYP 0000 0012 3456 7890     |            1
Saudi Arabia   | SA44 2000 0001 2345 6789 1234     |            1
Spain          | ES79 2100 0813 6101 2345 6789     |            1
Switzerland    | CH56 0483 5012 3456 7800 9        |            1
United Kingdom | GB98 MIDL 0700 9312 3456 78       |            1

db <>提琴here

答案 1 :(得分:0)

您的代码,已编辑:

CREATE OR REPLACE TRIGGER T_CHECKIBAN
    BEFORE INSERT OR UPDATE
    ON PAYMENTS
    FOR EACH ROW
BEGIN
    IF CheckIBANNUMMBER(:new.IBAN) = 0 /* new --> :new, CHECKIBAN -- > CheckIBANNUMMBER*/
    THEN
        raise_application_error(-20500, 'Wrong IBANNUMMBER');
    END IF; /* added END IF */
END;

我删除了双引号,因为只有在要使用小写字符时才需要双引号。

还要注意,您的函数给出了ResulText,而它似乎应该返回lResult;甚至,您正在使用varchar2返回值来处理数字。

如果只希望此功能检查IBAN是否正确,则应按以下方式编辑功能(ResulText在这里无效):

create or replace FUNCTION CheckIBANNUMMBER(
      pIBAN IN VARCHAR2
    ) RETURN number IS /* return a number */
    ...
      RETURN(lResult); /* return 0/1 */
    END CheckIBANNUMMBER;