sql-server - 迁移到 SQL Server 2017:同一查询有时不返回任何行
问题描述
我刚刚从 SQL Server 2014(仅开发环境)升级到 SQL Server 2017,并注意到一个非常奇怪的行为:相同的查询对两台服务器有两种不同的影响。
查询已被重写并减少到示例的最小值:
SELECT * FROM eth_Mandate M
WHERE customerId = 1
AND M.isActive = 1
该字段isActive
是一个计算列(如果授权处于活动状态,则返回 1,否则返回 0)定义为:
[isActive] AS (CASE WHEN GETDATE() >= ISNULL([man_start],GETDATE()) AND GETDATE() <= isnull(dateadd(day,(1),[man_stop]),GETDATE()) THEN 1 ELSE 0 END)
对于 SQL Server 2014,它始终有效。对于 2017 年,4-5 次它不返回任何行,因为有时isActive
计算为 0。
为什么?我已经深入搜索并想分享它,以便您可以在我根据测试理解的解释下方找到我自己的答案。
解决方案
问题在于,在 ISNULL 中,我们有一个 datetime2(字段 man_stop)和 getdate 作为后备(返回一个日期时间),并且显然 ISNULL 基于第一个参数转换了第二个参数......因此,我们正在比较 getdate( ) 作为 datetime,getdate() 作为 datetime2。请注意,man_stop(客户授权结束)大多数时候不是空的,因为授权正在运行。
然后,你必须看看这篇文章,特别是关于这句话:
至于时间部分,SQL Server 从 0 开始,并在午夜后每 0.003 秒递增数值。意味着什么 ?如果时间是 12:15:00:002 实际上存储为 12:15:00:003 但与 12:15:00.002 相比所以不相等!
解决方案 1
因此,为了解决 SQL Server 2017 的问题(尽管 SQL Server 2014 没有问题),我只是将我的字段 datetime2 转换为 datetime 以便与 getdate() 作为 datetime 进行正确比较:
[isActive] AS (case when getdate()>=isnull(CONVERT([datetime],[man_start]),getdate()) AND getdate()<=isnull(dateadd(day,(1),CONVERT([datetime],[man_stop])),getdate()) then (1) else (0) end)
解决方案 2
或者我也可以将所有日期时间转换为 datetime2。使用返回 datetime2(7) 的 SYSDATETIME() 代替 GETDATE()。由于我的两个字段只是 datetime2 (未定义精度),因此比较更糟......但是当将 datetime2 转换为 datetime2(7) 时,它运行起来就像一个魅力。所以它变成:
(case when SYSDATETIME () >= isnull(convert(datetime2(7), [man_start]),SYSDATETIME()) AND SYSDATETIME() <= isnull(dateadd(day,(1),convert(datetime2(7), [man_stop])),SYSDATETIME()) then (1) else (0) end)
解决方案 3
将数据库兼容级别更改为 120 或更低,以在比较日期时间和日期时间 2 类型时保留旧行为。SQL Server 2016 中引入了这一重大更改。
解决方案 4(已应用解决方案)
使用相同类型的数据,避免类型转换。更改字段的类型man_start
并更改man_stop
为 datetime2(7)(而不是 datetime2,即 datetime2(3))以匹配 SYSDATETIME() 函数返回的类型 (datetime2(7))。
ALTER TABLE myTable ALTER COLUMN [man_start] datetime2(7) NULL
ALTER TABLE myTable ALTER COLUMN [man_stop] datetime2(7) NULL
所以我的计算列变为: [isActive] AS (CASE WHEN SYSDATETIME() >= ISNULL([man_start],SYSDATETIME()) AND SYSDATETIME() <= isnull(dateadd(day,(1),[man_stop]),SYSDATETIME ()) 然后 1 否则 0 结束)
优点:保持兼容级别,避免类型转换(更好的性能)
推荐阅读
- c - 父进程如何等待所有子进程终止
- mysql - 如何在 Mysql Workbench 中运行脚本?
- ios - 与 AUTH0 iOS 的通用链接
- python - 如何将这个复杂的 SQL 转换为 Django 模型查询?
- ios - 如何避免swift 3中的错误无效重新声明?
- puppeteer - 可以使用 puppeteer 预渲染 SPA 吗?
- wordpress - WordPress 音频播放器在不同的浏览器上有所不同
- angular - 如何使用角度 4 中的自定义指令为同级文本添加颜色
- ruby-on-rails - 不允许的参数::email, :address
- python - 减小 Keras LSTM 模型的大小