选择子句上的 CASE 子句抛出 'SQLCODE=-811, SQLSTATE=21000' 错误
Posted
技术标签:
【中文标题】选择子句上的 CASE 子句抛出 \'SQLCODE=-811, SQLSTATE=21000\' 错误【英文标题】:CASE Clause on select clause throwing 'SQLCODE=-811, SQLSTATE=21000' Error选择子句上的 CASE 子句抛出 'SQLCODE=-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 子句抛出 'SQLCODE=-811, SQLSTATE=21000' 错误的主要内容,如果未能解决你的问题,请参考以下文章