【问题标题】:Weird T-SQL DATEADD Overflow奇怪的 T-SQL DATEADD 溢出
【发布时间】:2017-11-26 03:39:54
【问题描述】:

我正在尝试将数字 (16,8) 添加到 smalldatetime。我收到一个我无法理解的溢出错误。

metric_value 是数字,business_date 是 smalldatetime。

SELECT
    rsda.name
    , business_date
    , metric_value
    , DATEADD(dd, metric_value, business_date) AS o_dt
    , metric.name
FROM [redacted] met
INNER JOIN [redacted] rsda ON met.bu_id = rsda.data_accessor_id
INNER JOIN Metric ON met.metric_id = metric.metric_id
WHERE CHARINDEX('Remodel', metric.name) > 0

Msg 517, Level 16, State 2, Line 1
Adding a value to a 'smalldatetime' column caused an overflow.

我意识到显而易见的答案是“您的某个日期添加到 9999 年之后”,但这里的情况并非如此。所有日期都是 2017 年,所有数字都是 100 以下的整数,至少在 WHERE 子句为真时是这样。

met 表有大量与我的“改造”标准无关的数据,我想知道这是否会导致错误。这是一张奇怪的桌子,但我无法控制它的设计。是否有可能在应用我的 WHERE 子句之前发生部分 DATEADD 过程?我无法想象还会发生什么。

编辑。当我删除 DATEADD 字段时:

这是“ORDER BY business_date ASC”的第一行

+----------+---------------------+--------------+-------------+
|   name   |    business_date    | metric_value |    name     |
+----------+---------------------+--------------+-------------+
| 466 - 94 | 2017-03-13 00:00:00 | 59.00000000  | FullRemodel |
+----------+---------------------+--------------+-------------+

ORDER BY business_date DESC

+----------+---------------------+-------------+-------------+
| 440 - 87 | 2017-07-31 00:00:00 | 38.00000000 | FullRemodel |
+----------+---------------------+-------------+-------------+

按 metric_value ASC 排序

+----------+---------------------+------------+----------------+
| 471 - 05 | 2017-05-01 00:00:00 | 0.00000000 | PartialRemodel |
+----------+---------------------+------------+----------------+

按 metric_value DESC 排序

+----------+---------------------+-------------+-------------+
| 466 - 86 | 2017-03-13 00:00:00 | 59.00000000 | FullRemodel |
+----------+---------------------+-------------+-------------+

【问题讨论】:

  • smalldatetime 只到 2079 年 6 月 6 日
  • 好的,很高兴知道。但在这种情况下,任何日期都不会超过 2017 年。
  • 我会删除 DATEADD() 字段并查看返回的数据,看看 metric_value 或 business_date 字段中是否存在意外情况。
  • 我已经这样做了。但我会用“证明”编辑我原来的问题
  • “是否有可能在应用我的 WHERE 子句之前发生了部分 DATEADD 过程?”。是的。同stackoverflow.com/q/7192524/73226

标签: tsql


【解决方案1】:

您需要在 DATEADD() 函数中将 'dd' 替换为 DAY。

【讨论】:

  • 虽然我同意将 DAY、YEAR、MONTH 等放在 DATEADD 函数中,但“dd”仍然是“day”的完全有效的缩写。
  • 是的,查询还是失败。
【解决方案2】:

是否有可能在应用我的 WHERE 子句之前发生了部分 DATEADD 过程?

是的。计算表达式的计算标量可以在被过滤掉之前在行上运行。关于这个主题的一些链接在my answer here

您可以使用CASE 表达式仅在两个输入会导致 smalldatetime 范围内的有效日期时才执行 dateadd。

CASE 
 WHEN metric_value BETWEEN datediff(day, business_date, '1900-01-01') 
                       AND datediff(day, business_date, '2079-06-06')
 THEN 
  DATEADD(day, metric_value, business_date) 
END

【讨论】:

  • 感谢您的其他评论中的链接。
  • 这是一个很好的防止错误的方法,但是,如果用户对数据有控制权,我仍然认为应该将这个逻辑放在WHERE子句中找到坏行,然后修复数据。
【解决方案3】:

或者你也可以使用(在 SELECT 中)

CASE 
  WHEN metric_value <= datediff(day, GETDATE(), '20790606') 
  THEN DATEADD(day, metric_value, business_date) 
  ELSE NULL 
END AS o_dt

在日期时间上使用 BETWEEN 会很麻烦,所以我尽量避免。


编辑:根据 Martin 在下面的评论,我不再认为这是一个有效的解决方案。

结合@Martin Smith 的回答,您还可以更改您的 查询到子查询,它应该过滤掉那些 导致错误。

SELECT t1.rsda_name
    , t1.business_date
    , t1.metric_value
    , DATEADD(day, t1.metric_value, t1.business_date) AS o_dt
    , t1.metric_name
FROM (
    SELECT
        rsda.name AS rsda_name
        , business_date
        , metric_value
        , metric.name AS metric_name
    FROM [redacted] met
    INNER JOIN [redacted] rsda ON met.bu_id = rsda.data_accessor_id
    INNER JOIN Metric ON met.metric_id = metric.metric_id
    WHERE CHARINDEX('Remodel', metric.name) > 0
) t1

【讨论】:

  • 对您使用 06/06/2079 作为日期的原因发表评论可能不是一个坏主意。
  • 这不是一个安全的解决方案。计算标量可以推送到子查询中。见connect.microsoft.com/SQLServer/feedback/details/537419/…
  • @MartinSmith 有趣。不知道那个。我将为未来的用户编辑我的答案。 :-)
  • @MartinSmith 我想这不是一个有趣的错误。 :-/
猜你喜欢
  • 1970-01-01
  • 2012-02-14
  • 2012-06-28
  • 1970-01-01
  • 1970-01-01
  • 2014-03-03
  • 2012-09-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多