select子句中的CASE子句抛出' SQLCODE = -811,SQLSTATE = 21000'错误

时间:2014-09-27 07:07:23

标签: sql db2 case

此查询在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);

2 个答案:

答案 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值(这意味着您知道它存在)。如果我们不需要表中的任何内容(包括该表中的行数),通常最好将其从语句中删除:这样做可以简化数据库需要完成的工作。

相关问题