使用 SQL / PL SQL 验证日期 [关闭]

Posted

技术标签:

【中文标题】使用 SQL / PL SQL 验证日期 [关闭]【英文标题】:Verify date with SQL / PL SQL [closed] 【发布时间】:2021-09-11 03:44:26 【问题描述】:

我在验证代码中的日期时遇到问题。它并没有按应有的方式工作。

错误的部分与此有关。

ADD_COMPLEX_SALE_TO_DB - GENERATED UNEXPECTED EXCEPTION
ADD_COMPLEX_SALE_TO_DB - DATE NOT VALID EXCEPTION INCORRECT (WRONG MONTH)
ADD_COMPLEX_SALE_TO_DB - DATE NOT VALID EXCEPTION INCORRECT (WRONG DAY)

我的测试代码是这样的。我有一个不同的输出块,没有问题。

CREATE OR REPLACE PROCEDURE ADD_COMPLEX_SALE_TO_DB (pcustid Number, pprodid Number, pqty Number, pdate Varchar2) AS
    QTY_OUTSIDE_RANGE EXCEPTION;
    STATUS_NOT_OK EXCEPTION;
    INVALID_SALE_DATE EXCEPTION;
    vCUST CUSTOMER%ROWTYPE;
    vPROD PRODUCT%ROWTYPE;
    SALEID SALE%ROWTYPE;
    vPROD_ERROR INT := 0;
BEGIN
    IF 1 > pqty OR pqty > 999 THEN
    RAISE QTY_OUTSIDE_RANGE;
    END IF;
    
    SELECT * INTO vCUST
    FROM CUSTOMER
    WHERE CUSTID = pcustid;
    
    vPROD_ERROR := 1; 
    
    IF vCUST.STATUS != 'OK' THEN
    RAISE STATUS_NOT_OK;
    END IF; 

    SELECT * INTO vPROD
    FROM PRODUCT
    WHERE PRODID = pprodid;
    
    IF (LENGTH(pdate) <> 8) THEN 
        RAISE INVALID_SALE_DATE;
    END IF;
    
    INSERT INTO SALE (SALEID, CUSTID, PRODID, QTY, PRICE, SALEDATE) 
    VALUES (SALE_SEQ.NEXTVAL, pcustid, pprodid, pqty, vPROD.SELLING_PRICE, pdate); 
        
    UPD_CUST_SALESYTD_IN_DB(pcustid, (pqty*vPROD.SELLING_PRICE));   
    UPD_PROD_SALESYTD_IN_DB(pprodid, (pqty*vPROD.SELLING_PRICE));

EXCEPTION
    WHEN QTY_OUTSIDE_RANGE THEN
        RAISE_APPLICATION_ERROR (-20234, 'Sale Quantity outside valid range');
    WHEN STATUS_NOT_OK THEN 
        RAISE_APPLICATION_ERROR(-20245, 'Customer status is not OK');
    WHEN INVALID_SALE_DATE THEN
        RAISE_APPLICATION_ERROR(-20252, 'Date not valid');
    WHEN NO_DATA_FOUND THEN
        IF vPROD_ERROR = 0 THEN
            RAISE_APPLICATION_ERROR (-20263, 'Customer ID not found');
        ELSE 
            RAISE_APPLICATION_ERROR (-20271, 'Product ID not found');
        END IF;
    WHEN OTHERS THEN
        RAISE_APPLICATION_ERROR(-20000, SQLERRM);
END;

这是表创建语句。

CREATE TABLE CUSTOMER (
CUSTID  NUMBER
, CUSTNAME  VARCHAR2(100)
, SALES_YTD NUMBER
, STATUS    VARCHAR2(7)
, PRIMARY KEY   (CUSTID) 
);
CREATE TABLE PRODUCT (
PRODID  NUMBER
, PRODNAME  VARCHAR2(100)
, SELLING_PRICE NUMBER
, SALES_YTD NUMBER
, PRIMARY KEY   (PRODID)
);
CREATE TABLE SALE (
SALEID  NUMBER
, CUSTID    NUMBER
, PRODID    NUMBER
, QTY   NUMBER
, PRICE NUMBER
, SALEDATE  DATE
, PRIMARY KEY   (SALEID)
, FOREIGN KEY   (CUSTID) REFERENCES CUSTOMER
, FOREIGN KEY   (PRODID) REFERENCES PRODUCT
);

我怎样才能开始测试日期是否有效?看来我需要以某种方式分解并分别测试月/日/年,但我不确定如何最好地做到这一点。

【问题讨论】:

Oracle 没有例外“日期无效异常错误(错误月份)”。这似乎是你提出的一个例外。但是您发布的代码不会引发这样的异常,所以我猜您没有显示一些额外的代码。我不确定为什么您的程序中的pDatevarchar2 而不是date。如果您要使用varchar2 参数,您应该在insert 语句中通过带有显式格式掩码的to_date 将其显式转换为date,该格式掩码反映您要使用的字符串的格式路过。 由于用户提供日期信息的方式,在 varchar2 中输入 pDate, 要验证字符串是否可以转换为日期,请尝试转换它。您的 Oracle 版本是什么?从 12.2 开始,您可能会查看 validate_conversion 和 to_date 的 on conversion error 子句。 顺便说一句,WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20000, SQLERRM); 没有添加任何值。如果您没有捕获异常,则默认行为是使过程失败并返回一个错误堆栈,其中包含比您的版本更多的信息。 如果您要使用varchar2 参数,您应该通过to_date 在插入语句中将其显式转换为日期,并使用反映格式的显式格式掩码您传入的字符串。不知道您希望用户传入的字符串格式是什么,我无法建议您应该使用什么格式掩码。 【参考方案1】:

不要通过检查日期来检查日期。 01/01/21 是有效日期,但 99/01/21 不是。 oracle 数据库将尝试进行隐式转换,但将字符串更改为日期并不是一个好习惯。

既然你得到一个字符串,那么传递数据的人必须知道传递日期的格式。如果是 2021 年 2 月 9 日,他会传递 09/02/21, 02 /09/21、09-FEB-21 或 09-FEB-2021 ?所有这些都是有效日期,因此您应该假设传入日期参数​​的format 日期是已知的。使用已知的格式,您仍然可以获得无效的日期。假设您的日期格式是 DD/MM/YY,那么 09/02/21 是有效日期,但 50/02/21 不是,09/50/21 也不是。

使用TO_DATE 将字符串转换为数据类型DATE。 Oracle 有几个与日期相关的例外情况。

例子:

DECLARE
  l_date_input VARCHAR2(20) := '50/02/21';
  l_date DATE;
BEGIN
  l_date := TO_DATE(l_date_input,'DD/MM/YY');
END;
/
ORA-01847: day of month must be between 1 and last day of month
ORA-06512: at line 5
01847. 00000 -  "day of month must be between 1 and last day of month"

你自己试试吧。将 '50/02/21' 更改为 '09/50/21' 并查看引发了什么异常。

现在,让我们将其放在一个仅处理分配的日期部分的示例中。请注意,此示例是一个匿名 pl/sql 块,但可以轻松地将其更改为过程。 pragma EXCEPTION_INIT 允许 association of a user-defined exception name with an error code 因此下面示例中引发的错误可以映射到您自己的异常。

DECLARE
  l_date_input VARCHAR2(2000);
  l_date DATE;
  
  --exception thrown: ORA-01843: not a valid month
  e_not_a_valid_month EXCEPTION; 
  PRAGMA EXCEPTION_INIT (e_not_a_valid_month, -01843); 
  --exception thrown:  ORA-01847: day of month must be between 1 and last day of month
  e_not_a_valid_day EXCEPTION; 
  PRAGMA EXCEPTION_INIT (e_not_a_valid_day, -01847);   

BEGIN
  l_date_input := '01/99/21';
  l_date :=  TO_DATE(l_date_input,'DD/MM/YY');
EXCEPTION 
  WHEN e_not_a_valid_month THEN
    RAISE_APPLICATION_ERROR (-20234, 'DATE NOT VALID EXCEPTION INCORRECT (WRONG MONTH)');
  WHEN e_not_a_valid_day THEN
    RAISE_APPLICATION_ERROR (-20235, 'DATE NOT VALID EXCEPTION INCORRECT (WRONG DAY)');
  WHEN OTHERS THEN
    RAISE_APPLICATION_ERROR (-20236, 'GENERATED UNEXPECTED EXCEPTION');
END;
/
ORA-20234: DATE NOT VALID EXCEPTION INCORRECT (WRONG MONTH)
ORA-06512: at line 17

【讨论】:

以上是关于使用 SQL / PL SQL 验证日期 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL 脚本验证

对 apex oracle 数据库应用程序表单进行 pl sql 验证

使用 PL/SQL 的日期和条件

PL/SQL 日期插入

PL/SQL oracle,使用日期和时间的测试窗口

PL/SQL Developer 替代方案 [关闭]