DB2 SQL:Function在SELECT语句中起作用,但在WHERE子句中起作用

时间:2016-10-26 21:45:29

标签: sql db2

我有一个像这样设置的表:

**dNum | dType | dCode | dChar**
    1  |  blah |  foo  | xxxx
    1  |  blah |  foo2 | 10/11/2016
    2  |  blah |  foo  | xxxx
    2  |  blah |  foo2 | 10/19/2016

......等等。

对于每个唯一的dNum,我需要找到dCode等于" foo2"然后当dChar 完全 15天前。我的问题是从15天前收到日期。我目前的代码如下:

SELECT dNum
,dChar
FROM thing.table boo
WHERE dCode = 'foo2' AND
      DAYS(CURRENT_DATE) - DAYS(DATE(dChar)) = 15

当我尝试运行此查询时,我收到以下错误:"日期,时间或时间戳字符串中的值无效。"然而,我更加困惑的是,当我将DAYS(CURRENT_DATE) - DAYS(DATE(dChar))作为一个字段放在我的SELECT语句中但不放在WHERE子句中时,它可以正常工作。以下代码对我有用:

SELECT dNum
,dChar
,DAYS(CURRENT_DATE) - DAYS(DATE(dChar)) AS TEST1
FROM thing.table boo
WHERE dCode = 'foo2'

dChar是一个CHAR(25)变量,并非所有值都是日期,但同样,当我的代码不在WHERE子句中时,这似乎不会影响任何事情。

有人可以帮我弄清楚这里发生了什么吗?

3 个答案:

答案 0 :(得分:0)

如果您将where代码更改为:

会发生什么情况
WHERE dCode = 'foo2' AND
      (case when dcode = 'foo2'
            then DAYS(CURRENT_DATE) - DAYS(DATE(dChar))
       end) = 15

我意识到这是非常神秘的,但它保证仅在dcode = 'foo2'时评估该功能。可能发生的事情是,当这个条件成立时,这些值都是正确的,但问题在于其他地方。

答案 1 :(得分:0)

显然,dChar等于“foo2”的行中的dCode值是日期的有效表示。由于在已应用所有WHERE谓词后处理选择列表,因此不会导致任何问题。

另一方面,如果您将DATE(dChar)放在WHERE子句中,则可能会遇到无法转换为日期的dChar值。

Gordon Linoff建议的解决方案可能会或可能不会起作用(可能是后者),具体取决于优化程序如何决定处理CASE表达式。

编辑:

你的问题是“发生了什么事?”,所以现在你知道了。如果您想更进一步并找到可靠的解决方法,一个选项是创建一个用户定义的函数,该函数可以优雅地处理无效日期并使用它来代替DATE()。该函数可能类似于

create function to_date_safe (str varchar(20)) 
returns date 
deterministic 
no external action contains sql 
begin 
  declare continue handler for sqlstate '22007' -- on conversion error
    return null; 
  return date(str); 
end

您的WHERE子句看起来像

WHERE dCode = 'foo2' AND
  DAYS(CURRENT_DATE) - DAYS(to_date_safe(dChar)) = 15

您可能需要将'22007'替换为转换错误时返回给您的实际SQLSTATE值。

答案 2 :(得分:0)

如果使用嵌套查询过滤两次,则可以避免此问题(尝试在过滤掉非日期字符串之前解析它们)。

SELECT
    *
FROM
(
    SELECT
        dNum,
        dChar
    FROM
        thing.table boo
    WHERE
        dCode = 'foo2'
        AND dChar <> ''
)
    foo2
WHERE
    DAYS(CURRENT_DATE) - DAYS(DATE(dChar)) = 15

<强> 修改

或者,甚至更好,重新排序您的查询。将您的日期转换为字符串,而不是相反。然后可以使用索引,非日期格式值不会导致问题。

WHERE
        dCode = 'foo2'
    AND dChar = CHAR(CURRENT_DATE - 15 DAYS)