oracle 执行多个案例条件并给出错误

k4ymrczo  于 12个月前  发布在  Oracle
关注(0)|答案(2)|浏览(86)

我正在使用以下查询

SELECT d.mon,
CASE
WHEN MAX(CASE WHEN d.DRR = '80+' THEN 9999 ELSE TO_NUMBER(substr(d.DRR,1,INSTR(d.DRR, '-') - 1)) END) = 9999 THEN '80+'
WHEN MAX(CASE WHEN d.DRR = 'CURR' THEN 0 ELSE TO_NUMBER(substr(d.DRR,1,INSTR(d.DRR, '-') - 1)) END) = 0 THEN 'CURR'
ELSE(
 TO_CHAR(max(TO_NUMBER(substr(d.DRR,1,INSTR(d.DRR, '-') - 1)))) || '-' ||
 TO_CHAR(MAX( TO_NUMBER(substr(d.DRR,INSTR(d.DRR, '-') +1)) ))))
END
AS bill
FROM table d
GROUP BY d.mon
ORDER BY d.mon

但它给我的是无效的数字。我有以下数据集:
| Mon| DRR|
| --|--|
| 1 |80岁以上|
| 1 |1-30|
它应导致:
| Mon| DRR|
| --|--|
| 1 |80岁以上|
它不会工作,直到我添加以下条件:

TO_CHAR(MAX(CASE WHEN d.DRR = 'CURR' THEN 0 WHEN d.DRR = '80+' THEN 1 ELSE TO_NUMBER(substr(d.DRR,INSTR(d.DRR, '-') +1)) END))

为什么我需要添加这个条件,即使它满足了第一个条件。

tgabmvqs

tgabmvqs1#

当然,你得到了 * 无效号码 * 错误。这是因为你试图将to_number函数应用于substr的结果:

SQL> WITH
  2     table1 (mon, drr)
  3     AS
  4        (SELECT 1, '80+' FROM DUAL
  5         UNION ALL
  6         SELECT 1, '1-30' FROM DUAL)
  7  SELECT SUBSTR (d.DRR, INSTR (d.DRR, '-') + 1)    --> this
  8    FROM table1 d;

SUBS
----
80+            --> this is where TO_NUMBER fails
30

SQL>

这个查询应该做什么?情况看起来有点“复杂”。是否需要使用max功能?如果是,为什么?你在比较两个值,所以也许你只需要使用greatest?你能用简单的英语解释一下你想解决的问题吗?哪些规则必须应用于 * 源 * 数据以获取 * 目标 * 数据?也许你的问题是(重要的是?))简化。

7fhtutme

7fhtutme2#

CASE表达式使用短路逻辑,因此如果第一个WHEN表达式匹配,则后续的WHENELSE子句将不会被计算。
但是,当您使用聚合函数时,解析整个结果集以评估第一个WHEN子句中的聚合,然后重新解析整个结果集以评估第二个WHEN子句,等等CASE表达式的每个子句,这是效率低下的;相反,SQL引擎将解析结果集一次并计算所有聚合的值,然后将使用聚合的结果应用CASE表达式,这意味着将为所有聚合计算所有行。
如果您尝试不使用聚合:

SELECT mon,
       CASE
       WHEN CASE WHEN drr = '80+' THEN 1 END = 1
       THEN '80+'
       WHEN CASE WHEN drr = 'CURR' THEN 1 END = 1
       THEN 'CURR'
       ELSE TO_NUMBER(SUBSTR(drr, 1, INSTR(drr, '-') - 1))
            || '-'
            || TO_NUMBER(SUBSTR(drr, INSTR(drr, '-') + 1))
       END AS bill
FROM   table_name
ORDER BY mon;

然后,查询成功完成,并应用短路逻辑,以便在WHEN子句中的任何一个匹配时都不计算ELSE子句。但是,对于聚合,它不起作用。
如果你想修复它,那么你需要防止不匹配x-y模式的字符串;您可以:
使用正则表达式:

SELECT mon,
       CASE
       WHEN MAX(CASE WHEN drr = '80+' THEN 1 END) = 1
       THEN '80+'
       WHEN MAX(CASE WHEN drr = 'CURR' THEN 1 END) = 1
       THEN 'CURR'
       ELSE MAX(TO_NUMBER(REGEXP_SUBSTR(drr, '^(\d+)-(\d+)$', 1, 1, NULL, 1)))
            || '-'
            || MAX(TO_NUMBER(REGEXP_SUBSTR(drr, '^(\d+)-(\d+)$', 1, 1, NULL, 2)))
       END AS bill
FROM   table_name
GROUP BY mon
ORDER BY mon;

或者使用INSTR检查字符串中是否有连字符:

SELECT mon,
       CASE
       WHEN MAX(CASE WHEN drr = '80+' THEN 1 END) = 1
       THEN '80+'
       WHEN MAX(CASE WHEN drr = 'CURR' THEN 1 END) = 1
       THEN 'CURR'
       ELSE MAX(CASE WHEN INSTR(drr, '-') > 0 THEN TO_NUMBER(SUBSTR(drr, 1, INSTR(drr, '-') - 1)) END)
            || '-'
            || MAX(CASE WHEN INSTR(drr, '-') > 0 THEN TO_NUMBER(SUBSTR(drr, INSTR(drr, '-') + 1)) END)
       END AS bill
FROM   table_name
GROUP BY mon
ORDER BY mon;

或者过滤掉之前的匹配项:

SELECT mon,
       CASE
       WHEN MAX(CASE WHEN drr = '80+' THEN 1 END) = 1
       THEN '80+'
       WHEN MAX(CASE WHEN drr = 'CURR' THEN 1 END) = 1
       THEN 'CURR'
       ELSE MAX(CASE WHEN drr NOT IN ('80+', 'CURR') THEN TO_NUMBER(SUBSTR(drr, 1, INSTR(drr, '-') - 1)) END)
            || '-'
            || MAX(CASE WHEN drr NOT IN ('80+', 'CURR') THEN TO_NUMBER(SUBSTR(drr, INSTR(drr, '-') + 1)) END)
       END AS bill
FROM   table_name
GROUP BY mon
ORDER BY mon;

其中,对于样本数据:

CREATE TABLE table_name (mon, DRR) AS
  SELECT 1, '80+'  FROM DUAL UNION ALL
  SELECT 1, '1-30' FROM DUAL;

所有输出:
| Mon|法案|
| --|--|
| 1 |80岁以上|
fiddle

相关问题