此查询在Oracle中运行良好。但它不适用于DB2。它正在抛出
当DB2 SQL错误:SQLCODE = -811,SQLSTATE = 21000,SQLERRMC = null,DRIVER = 3.61.65
THEN
子句下的子查询返回2行时出现错误。
然而,我的问题是为什么它会首先执行,因为我的WHEN子句总是变为假。
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);
答案 0 :(得分:2)
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);
答案 1 :(得分:1)
嗯......你两次运行相同的查询。我感觉你并没有在 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
子句 - 始终明确限定您的连接。首先,它很容易错过一个条件你应该加入)
......从这一点来看,很明显你的陈述是相同的&#39;在两个查询中,并显示您正在尝试的内容 - 如果数据集有一行,则返回它,否则结果应为null。
输入HAVING
子句:
HAVING COUNT(*) = 1
这实际上是聚合的WHERE
子句(MAX(...)
或此处COUNT(...)
等函数)。当您想确保整个集的某些方面与给定条件匹配时,这非常有用。在这里,我们希望确保只有一行,因此使用COUNT(*) = 1
作为条件是合适的;如果有更多(或更少!可能是0行!),该组将被丢弃/忽略。
当然,使用HAVING
表示我们正在使用聚合,通常的规则适用:所有列都必须位于GROUP BY
(在这种情况下实际上是一个选项),或集合函数。因为我们只想要/期望一行,所以我们可以作弊,只需指定一个简单的MAX(...)
来满足解析器。
此时,如果初始数据中只有一行,则新子查询返回一行(包含一列),否则返回无行(此部分很重要)。但是,我们实际上需要返回一行。
FROM SYSIBM/SYSDUMMY1
这是所有DB2安装上的一个方便的虚拟表。它有一行,其中一列包含'1'
(字符&#39; 1&#39;,而不是数字1)。我们实际感兴趣的是它只有一行...
LEFT JOIN (SELECT ... )
ON 1 = 1
A LEFT JOIN
获取前一个集合中的每一行(前面表格中的所有连接行),并将它乘以下一个表格引用中的每一行,在右边的集合中乘以1 (新引用,我们的子查询)没有行。 (这与常规(INNER) JOIN
的工作方式不同,在没有行的情况下乘以0)当然,我们只有可能有1行,所以&#39; s只会是最多一个结果行。我们需要有一个ON ...
子句,但是没有数据可以实际关联引用,因此使用了一个简单的始终为真的条件。
要获取我们的数据,我们只需要获得相关列:
SELECT Current_Stop.facility_alias_id
...如果有一行数据,则返回。如果存在其他一些行数,则HAVING
子句会抛出该集合,而LEFT JOIN
会导致使用null
(无数据)值填充该列
那我为什么要删除对Shipment
的引用?首先,您没有使用表中的任何数据 - 结果集中唯一的列来自子查询。我也有充分的理由相信在这种情况下只返回一行 - 您指定一个shipment_id
值(这意味着您知道它存在)。如果我们不需要表中的任何内容(包括该表中的行数),通常最好将其从语句中删除:这样做可以简化数据库需要完成的工作。