选择子句上的 CASE 子句抛出 'SQ​​LCODE=-811, SQLSTATE=21000' 错误

Posted

技术标签:

【中文标题】选择子句上的 CASE 子句抛出 \'SQ​​LCODE=-811, SQLSTATE=21000\' 错误【英文标题】:CASE Clause on select clause throwing 'SQLCODE=-811, SQLSTATE=21000' Error选择子句上的 CASE 子句抛出 'SQ​​LCODE=-811, SQLSTATE=21000' 错误 【发布时间】:2014-09-27 07:07:23 【问题描述】:

此查询在 Oracle 中运行良好。但它在 DB2 中不起作用。它在扔

DB2 SQL 错误:SQLCODE=-811,SQLSTATE=21000,SQLERRMC=null,DRIVER=3.61.65

THEN 子句下的子查询返回 2 行时出错。

但是,我的问题是为什么它会首先执行,因为我的 WHEN 子句总是变为 false。

SELECT 
CASE
WHEN (SELECT COUNT(1)
  FROM STOP ST,
    FACILITY FAC
  WHERE ST.FACILITY_ID     = FAC.FACILITY_ID
  AND FAC.IS_DOCK_SCHED_FAC=1
  AND ST.SHIPMENT_ID       = 2779) = 1
THEN
  (SELECT ST.FACILITY_ALIAS_ID
  FROM STOP ST,
    FACILITY FAC
  WHERE ST.FACILITY_ID     = FAC.FACILITY_ID
  AND FAC.IS_DOCK_SCHED_FAC=1
  AND ST.SHIPMENT_ID       = 2779
  )
ELSE NULL
END STAPPFAC
FROM SHIPMENT SHIPMENT
WHERE SHIPMENT.SHIPMENT_ID IN (2779);

【问题讨论】:

您使用的是哪个版本的 db2? 【参考方案1】:

SQL 标准不要求捷径评估(即 CASE 语句各部分的评估顺序)。 Oracle chooses to specify shortcut evaluation,但是 DB2 似乎没有这样做。

稍微为 DB2 重写您的查询(8.1+ 仅适用于子查询中的 FETCH)应该允许它运行(不确定您是否需要添加的 ORDER BY 并且目前没有要测试的 DB2)

SELECT 
CASE
WHEN (SELECT COUNT(1)
  FROM STOP ST,
    FACILITY FAC
  WHERE ST.FACILITY_ID     = FAC.FACILITY_ID
  AND FAC.IS_DOCK_SCHED_FAC=1
  AND ST.SHIPMENT_ID       = 2779) = 1
THEN
  (SELECT ST.FACILITY_ALIAS_ID
  FROM STOP ST,
    FACILITY FAC
  WHERE ST.FACILITY_ID     = FAC.FACILITY_ID
  AND FAC.IS_DOCK_SCHED_FAC=1
  AND ST.SHIPMENT_ID       = 2779
  ORDER BY ST.SHIPMENT_ID
  FETCH FIRST 1 ROWS ONLY
  )
ELSE NULL
END STAPPFAC
FROM SHIPMENT SHIPMENT
WHERE SHIPMENT.SHIPMENT_ID IN (2779);

【讨论】:

从语法上讲,ORDER BY 不是必需的,如果您在语句返回多行时未能指定它,它只会给您未定义的结果。鉴于只有一行(...除非有一些奇怪的并发问题,这会给这个版本带来其他问题...)您可以将ORDER BY 关闭。当然,考虑到您是按一个值键控的东西进行排序(给定WHERE 子句),在存在多行的情况下排序将是未定义的......【参考方案2】:

嗯...您正在运行相同的查询两次。我感觉您不是在sets(SQL 如何运行)中思考,而是以更程序化的形式(即,最常见的编程语言如何工作)。您可能想重写它以利用 RDBMS 的工作方式:

SELECT Current_Stop.facility_alias_id
FROM SYSIBM/SYSDUMMY1
LEFT JOIN (SELECT MAX(Stop.facility_alias_id) AS facility_alias_id
           FROM Stop
           JOIN Facility
             ON Facility.facility_id = Stop.facility_id
                AND Facility.is_dock_sched_fac = 1
           WHERE Stop.shipment_id = 2779
           HAVING COUNT(*) = 1) Current_Stop
       ON 1 = 1

(没有样本数据,因此未测试。根据其他需要还有其他几种编写方法) 这应该适用于所有 RDBMS。


那么这里发生了什么,为什么会这样呢? (为什么我删除了对Shipment 的引用?)

首先,让我们再看看您的查询:

CASE WHEN (SELECT COUNT(1)
           FROM STOP ST, FACILITY FAC
           WHERE ST.FACILITY_ID = FAC.FACILITY_ID
                 AND FAC.IS_DOCK_SCHED_FAC = 1
                 AND ST.SHIPMENT_ID = 2779) = 1
     THEN (SELECT ST.FACILITY_ALIAS_ID
           FROM STOP ST, FACILITY FAC
           WHERE ST.FACILITY_ID = FAC.FACILITY_ID
                 AND FAC.IS_DOCK_SCHED_FAC = 1
                 AND ST.SHIPMENT_ID = 2779)
     ELSE NULL END 

(首先,停止使用隐式连接语法 - 即逗号分隔的 FROM 子句 - 始终明确限定您的连接。一方面,很容易错过您应该加入的条件) ...从这一点很明显,您的语句在两个查询中都是“相同的”,并显示了您正在尝试的内容 - 如果数据集有一行,则返回它,否则结果应该为 null。

输入HAVING 子句:

HAVING COUNT(*) = 1

这本质上是一个用于聚合的WHERE 子句(类似MAX(...) 的函数,或者这里的COUNT(...))。当您想确保 整个 集合的某些方面与给定条件匹配时,这很有用。在这里,我们要确保只有一行,所以使用COUNT(*) = 1 作为条件是合适的;如果有更多(或更少!可能是 0 行!),则该集合将被丢弃/忽略。

当然,使用HAVING 意味着我们正在使用聚合,通常的规则适用:所有列必须要么在GROUP BY 中(在这种情况下实际上是一个选项),要么是聚合函数。因为我们只想要/期望一行,所以我们可以稍微作弊,只需指定一个简单的MAX(...) 即可满足解析器的要求。

此时,如果初始数据中只有一行,则新的子查询返回一行(包含一列),否则无行(这部分很重要)。但是,我们实际上需要返回一行。

FROM SYSIBM/SYSDUMMY1

这是一个适用于所有 DB2 安装的方便的虚拟表。它有一行,一列包含'1'(字符'1',不是数字1)。我们实际上对它只有一排这一事实感兴趣......

LEFT JOIN (SELECT ... )
       ON 1 = 1

LEFT JOIN 获取前面集合中的每一行(前面表中的所有连接行),并将其乘以下一个表引用中的每一行,如果右边的集合(新参考,我们的子查询)没有行。 (这与常规 (INNER) JOIN 的工作方式不同,后者在没有行的情况下乘以 0)当然,我们只有 也许 有 1 行,所以只会有最多一个结果行。我们需要有一个ON ... 子句,但没有数据可以在引用之间实际关联,因此使用了一个简单的始终为真条件。

要获取我们的数据,我们只需要获取相关列:

 SELECT Current_Stop.facility_alias_id

...如果有一行数据,则返回。在存在其他行数的情况下,HAVING 子句会抛出该集合,LEFT JOIN 会导致用null(无数据)值填充列。

那么我为什么要删除对Shipment 的引用?首先,您没有使用表中的任何数据 - 结果集中的唯一列来自子查询。我也有充分的理由相信在这种情况下只会返回一行 - 您指定了一个 shipment_id 值(这意味着您知道它存在)。如果我们不需要表中的任何内容(包括该表中的行数),通常最好将其从语句中删除:这样做可以简化 db 需要做的工作。

【讨论】:

以上是关于选择子句上的 CASE 子句抛出 'SQ​​LCODE=-811, SQLSTATE=21000' 错误的主要内容,如果未能解决你的问题,请参考以下文章

DB2 中的 CASE 子句语句

Oracle where 子句中的 case 表达式

如何使用 case 语句来确定存储过程中的 from 和 where 子句?

Where 子句 TSQL 中的 case 表达式

在 where 子句中使用 CASE

使用多 case 语句时分区顺序子句出错