无法在 Azure SQL 跨数据库查询中按日期时间类型进行筛选
Posted
技术标签:
【中文标题】无法在 Azure SQL 跨数据库查询中按日期时间类型进行筛选【英文标题】:Cannot Filter by DateTime Type in Azure SQL Cross-Database Queries 【发布时间】:2021-12-13 05:24:17 【问题描述】:在 Azure SQL 数据库中,我们使用外部表从另一个 Azure SQL 数据库中查询数据。按 DATETIME 列过滤外部表时,如果毫秒的最后一位为 3 或 7,则不返回任何行。如果最后一位为 0,则查询按预期工作。
示例:
/* MILLISECONDS ENDS WITH 0 - WORKS AS EXPECTED*/
DECLARE @myDate DATETIME = '2021-10-27 12:00:00.000';
SELECT * from dbo.ext_datetimetest where myDate = @myDate;
GO
/* MILLISECONDS ENDS WITH 3 OR 7 - RETURNS NOTHING*/
DECLARE @myDate DATETIME = '2021-10-27 12:00:00.003';
SELECT * from dbo.ext_datetimetest where myDate = @myDate;
GO
注意事项:
只有参数化查询受到影响。在 where 子句中硬编码日期时间值的任何查询都可以按预期工作。 只有跨数据库查询受到影响。直接在源数据库上运行查询按预期工作。 我们的代码多年来一直按预期工作,上周才开始出现这种行为。 这只发生在我们的测试和生产环境中。我们的开发环境对所有日期时间值都按预期工作。复制步骤:
/* EXECUTE IN DATABASE #1 */
CREATE TABLE dbo.datetimetest (myDate DATETIME NOT NULL);
GO
INSERT dbo.datetimetest (myDate)
VALUES
('2021-10-27 12:00:00.000')
,('2021-10-27 12:00:00.003')
,('2021-10-27 12:00:00.007')
,('2021-10-27 12:00:00.010')
;
GO
/* EXECUTE IN DATABASE #2 */
CREATE EXTERNAL TABLE dbo.ext_datetimetest ( myDate DATETIME NOT NULL)
WITH (DATA_SOURCE = [DATABASE #1], SCHEMA_NAME = N'dbo', OBJECT_NAME = N'datetimetest');
GO
/* SELECT ALL ROWS TO CONFIRM VALUES */
SELECT * FROM dbo.ext_datetimetest;
/* These all work because the filters are hardcoded */
SELECT * from dbo.ext_datetimetest where myDate = '2021-10-27 12:00:00.000';
SELECT * from dbo.ext_datetimetest where myDate = '2021-10-27 12:00:00.003';
SELECT * from dbo.ext_datetimetest where myDate = '2021-10-27 12:00:00.007';
SELECT * from dbo.ext_datetimetest where myDate = '2021-10-27 12:00:00.010';
GO
/* VARIABLES ONLY WORK IF LAST DIGIT IS 0 */
DECLARE @myDate DATETIME;
SET @myDate = '2021-10-27 12:00:00.000'; SELECT * from dbo.ext_datetimetest where myDate = @myDate; /* WORKS */
SET @myDate = '2021-10-27 12:00:00.003'; SELECT * from dbo.ext_datetimetest where myDate = @myDate; /* RETURNS NOTHING */
SET @myDate = '2021-10-27 12:00:00.007'; SELECT * from dbo.ext_datetimetest where myDate = @myDate; /* RETURNS NOTHING */
SET @myDate = '2021-10-27 12:00:00.010'; SELECT * from dbo.ext_datetimetest where myDate = @myDate; /* WORKS */
GO
【问题讨论】:
首先,用equals查询日期时间值是很不寻常的。我假设您知道datetime
日期类型仅存储有限的精度?来自文档“四舍五入到 .000、.003 或 .007 秒的增量”。
如果@myDate
在某处转换为datetime2
,那么2021-10-27 12:00:00.003
将变为2021-10-27 12:00:00.0033333
- 数据类型是否已更改?或者数据库兼容级别从<130
更改为>=130
?
我认为你在正确的轨道上。环境中一定发生了一些变化,但我不知道是什么。所有数据库都设置为兼容级别 130。sys.databases 中的所有设置在 Dev Test 和 Prod 之间都是相同的。
【参考方案1】:
作为一种可能的解决方法(不是解决根本问题),请尝试:
... where myDate = CONVERT(VARCHAR(23), @myDate, 21)
我推测日期值正在转换为跨数据库连接上的某种表示形式,该表示形式与精确比较所需的 1/300 秒精度不完全匹配。转换为字符串可能会传递等效于有效的文字(字符串)日期。但效率可能会受到影响。
【讨论】:
不幸的是,这没有帮助。我已经尝试过这个和几种变体,但没有运气。我同意您的推测,并愿意采用解决方法,但希望解决根本原因。 另一种选择(不漂亮)可能是指定一个范围:myDate BETWEEN DATEADD(ms, -2, @myDate) AND DATEADD(ms, 2, @myDate) 非常感谢您的建议。我也试过这个。使用 +/- 1ms 的范围是行不通的,而您建议的 +/- 2ms 可以获取目标值以及相邻值。在生产中,这种情况不太可能发生,但在我的重现步骤中,过滤 0.003 +/- 2ms 也会返回 .000 和 .007。 我目前的解决方法是生成动态 SQL 以将值嵌入到查询中。在修复之前,我将忽略我的查询计划缓存:(以上是关于无法在 Azure SQL 跨数据库查询中按日期时间类型进行筛选的主要内容,如果未能解决你的问题,请参考以下文章