【问题标题】:Handling exception in Oracle在 Oracle 中处理异常
【发布时间】:2018-11-16 09:55:42
【问题描述】:

我正在尝试将输入的年份解析为字符串(请不要让我开始 - 它就是这样)。但是,有些年份输入无法被 TO_NUMBER 解析。

WITH src AS (
        SELECT '2000' AS y FROM DUAL
  UNION SELECT '1991' AS y FROM DUAL
  UNION SELECT '20--' AS y FROM DUAL
  UNION SELECT '09' AS y FROM DUAL
  UNION SELECT '11' AS y FROM DUAL
  UNION SELECT '95' AS y FROM DUAL
)

BEGIN
  SELECT
    s.y,
    TO_NUMBER(s.y) AS p
  FROM src s
EXCEPTION
  WHEN INVALID_NUMBER THEN NULL
END

我从未在 Oracle 中进行过异常处理,如果这是一个如此基本的问题,我深表歉意。

在上面运行我的查询时,我得到ORA-00928: missing SELECT keyword,然后它突出显示BEGIN 关键字。通过搜索,我看到人们所做的就是使用BEGIN SELECT,这也是我正在做的事情。我猜我在其他地方搞砸了?

基本上我想做的是解析字符串,如果抛出异常,我会将其设置为NULL

编辑

我尝试了一种不同的方法并添加了一些分号,正如@DavidFaber 在下面评论的那样。

BEGIN
  SELECT
    s.y,
    TO_NUMBER(s.y) AS p
  FROM (
          SELECT '2000' AS y FROM DUAL
    UNION SELECT '1991' AS y FROM DUAL
    UNION SELECT '20--' AS y FROM DUAL
    UNION SELECT '09' AS y FROM DUAL
    UNION SELECT '11' AS y FROM DUAL
    UNION SELECT '95' AS y FROM DUAL
  ) s;
EXCEPTION
  WHEN INVALID_NUMBER THEN NULL;
END;

我现在收到另一个错误ORA-06550: line 2, column 3: PLS-00428: an INTO clause is expected in this SELECT statement

【问题讨论】:

  • 您似乎缺少一些分号 - 这不是一个简单的 SQL 查询,它是 PL/SQL
  • @lad2025 这似乎是一个很好的解决方案,但我们正在使用11g,所以看起来我不能使用它。
  • 不过要注意的一点是,在您的 Web 应用程序中解析这些日期(在您的情况下为年份)总是更好,并且只在数据库中存储和操作日期类型及其相关代码。重要的是,永远不要将日期和时间戳作为字符串存储在数据库中,如果您还没有这样做或计划这样做,那就太好了。
  • @KaushikNayak 我需要在查询中解析这些年份,因为我正在生成带有日期参数的报告。是的,我总是使用正确的数据类型,这只是我们仍在维护的一个非常古老的系统。没有设计出最好的方式,但它就是这样。

标签: sql oracle exception exception-handling parseint


【解决方案1】:

There's no exception-handling in SQL;

是的,没错,但是有像内联函数(Oracle 12c)这样的解决方法:

WITH FUNCTION safe_to_NUMBER(input IN VARCHAR2)
RETURN NUMBER IS
i NUMBER;
BEGIN
  i:= TO_NUMBER(input);

  RETURN i;
  EXCEPTION
  WHEN OTHERS THEN 
    RETURN NULL;
END;
SELECT  sub.y, safe_to_NUMBER(sub.y)
FROM (
    SELECT '2000' AS y FROM DUAL UNION ALL
    SELECT '1991' AS y FROM DUAL UNION ALL
    SELECT '20--' AS y FROM DUAL UNION ALL
    SELECT '09' AS y FROM DUAL UNION ALL
    SELECT '11' AS y FROM DUAL UNION ALL
    SELECT '95' AS y FROM DUAL
  ) sub;

结果:

Y    SAFE_TO_NUMBER(SUB.Y)
---- ---------------------
2000                  2000
1991                  1991
20--                      
09                       9
11                      11
95                      95

6 rows selected. 

我当然不会写这样的生产代码:)


正确方式(DEFAULT NULL ON CONVERSION ERROR - 从 Oracle12cR2 开始可用):

SELECT sub.y, TO_NUMBER(sub.y DEFAULT NULL ON CONVERSION ERROR) AS y
FROM (
    SELECT '2000' AS y FROM DUAL UNION ALL
    SELECT '1991' AS y FROM DUAL UNION ALL
    SELECT '20--' AS y FROM DUAL UNION ALL
    SELECT '09' AS y FROM DUAL UNION ALL
    SELECT '11' AS y FROM DUAL UNION ALL
    SELECT '95' AS y FROM DUAL
  ) sub;

输出:

Y             Y
---- ----------
2000       2000
1991       1991
20--           
09            9
11           11
95           95

6 rows selected. 

【讨论】:

    【解决方案2】:

    SQL 中没有异常处理;您需要创建一个 PL/SQL 块来处理异常(请注意,我将您的 UNIONs 更改为 UNION ALL):

    BEGIN
      WITH src AS (
        SELECT '2000' AS y FROM DUAL UNION ALL
        SELECT '1991' AS y FROM DUAL UNION ALL
        SELECT '20--' AS y FROM DUAL UNION ALL
        SELECT '09' AS y FROM DUAL UNION ALL
        SELECT '11' AS y FROM DUAL UNION ALL
        SELECT '95' AS y FROM DUAL
      )
      SELECT s.y, TO_NUMBER(s.y) AS p
        FROM src s;
    EXCEPTION
      WHEN INVALID_NUMBER THEN NULL;
    END;
    /
    

    但您可以使用正则表达式来执行“安全”数字转换,而不是使用 PL/SQL 块:

      WITH src AS (
        SELECT '2000' AS y FROM DUAL UNION ALL
        SELECT '1991' AS y FROM DUAL UNION ALL
        SELECT '20--' AS y FROM DUAL UNION ALL
        SELECT '09' AS y FROM DUAL UNION ALL
        SELECT '11' AS y FROM DUAL UNION ALL
        SELECT '95' AS y FROM DUAL
      )
      SELECT s.y, TO_NUMBER(REGEXP_SUBSTR(s.y, '^\d+'))
        FROM src s;
    

    上面会将值 20-- 转换为 20,这可能不是您想要的 - 在这种情况下,请尝试使用此模式 ^\d+$

      WITH src AS (
        SELECT '2000' AS y FROM DUAL UNION ALL
        SELECT '1991' AS y FROM DUAL UNION ALL
        SELECT '20--' AS y FROM DUAL UNION ALL
        SELECT '09' AS y FROM DUAL UNION ALL
        SELECT '11' AS y FROM DUAL UNION ALL
        SELECT '95' AS y FROM DUAL
      )
      SELECT s.y, TO_NUMBER(REGEXP_SUBSTR(s.y, '^\d+$'))
        FROM src s;
    

    希望这会有所帮助。

    【讨论】:

    • 我只想补充一点,Oracle 12c 的一种解决方法可能是WITH FUNCTION,然后我可以在内联函数中处理它。
    • 我在 PL/SQL 中运行了这个,我得到一个错误 ORA-06550: line 2, column 3: PLS-00428: an INTO clause is expected in this SELECT statement
    • @uom-pgregorio,是的,对此感到抱歉。您必须SELECT ... INTO 然后打印出结果,或进行更新等。但您不需要PL/SQL 块来完成此操作;查看我的编辑。
    • 谢谢。正则表达式解决方案似乎运行良好。
    猜你喜欢
    • 2010-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多