【问题标题】:SQL Trace/Profiler - Ambiguous date format being displayed (.NET Core/EF)SQL Trace/Profiler - 显示不明确的日期格式 (.NET Core/EF)
【发布时间】:2019-06-09 10:08:27
【问题描述】:

我们安装了一个使用英语(美国)语言的 SQL Server 2008R2 实例。 SSMS > 实例 > 属性 > 常规。

我们使用默认语言“英式英语”设置了登录。 SSMS > 安全 > 登录 > 用户 > 属性。

前端 .NET Core 应用程序通过表单/Web API 生成插入语句 (EF),它使用上面设置的登录创建“sp_executesql”系统存储过程。

使用 SQL Server Profiler,我可以看到生成的插入 SQL 包含'yyyy-mm-dd'. 格式的字符串日期,例如"2019-01-15 10:59:19.410"

在执行该语句之前,有一个 exec sp_reset_connection 后跟以下 SET 语句(我相信这与连接池共享有关,以确保每次登录都正确设置)。

-- network protocol: LPC
set quoted_identifier on
set arithabort off
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language British
set dateformat dmy
set datefirst 1
set transaction isolation level read committed

令我困惑的是,即使有yyyy-mm-dd 格式的日期,而环境设置为英式英语 (DMY),该语句也能成功运行。例如 - "2019-01-15 10:59:19.410"

我了解到,如果 DMY 是当前设置,“2019-01-15 10:59:19.410”将被解释为 2019 年第 15 个月的第一天。

例如这将失败,因为 SQL 将尝试将日期字符串解释为 yyyy-dd-mm

SET LANGUAGE [British English];

SELECT
    CAST('2019-01-13' AS DATETIME);

Msg 242, Level 16, State 3, Line 3 varchar 数据的转换 type 到 datetime 数据类型导致超出范围的值。

将语言设置为英语 (us_english) 将字符串解释更改为 MDY 并且上述语句有效。

我知道我可以使用明确的日期字符串,但我遇到的问题与 EF 前端生成的日期字符串有关。

我无法弄清楚为什么该语句运行成功。显然存在从日期字符串到 DATETIME 类型的隐式转换。

如果我从 SQL 探查器复制语句并粘贴到 SSMS 并尝试针对同一个数据库/同一个用户运行,那么它会像我预期的那样失败。

消息 8114,级别 16,状态 1,第 14 行错误转换数据类型 varchar 到日期时间。

所以...

我了解从字符串日期转换为 DATETIME 类型并在 SSMS 中运行查询获得一致的结果时发生了什么,但我不明白为什么在 SSMS 中失败的查询是从前端运行的。

如果没有很好地解释,我深表歉意。

【问题讨论】:

  • 个人建议使用 ISO 格式;那么无论语言设置如何,您都不会遇到问题。因此,对于日期,使用yyyyMMdd(即'20190115'),对于日期时间使用yyyy-MM-ddThh:mm:ss.ssssss(即'2019-01-05T11:46:17.0000000')。
  • 谢谢拉努。我同意你的说法,并且总是使用明确的日期字符串,但问题是我无法控制它,因为字符串是由实体框架生成的 yyyy-mm-dd
  • Profiler 在骗你。当执行采用DATETIME 类型参数的存储过程/准备语句时,它将以保证以独立于语言的方式工作的方式格式化参数值,但客户端代码将DATETIME 值作为独立于任何格式的类型值传递,而不是传递给sp_executesql 的字符串文字。这应该被认为是 Profiler 中的一个错误,因为它可以使用 few safe formats 之一。
  • 例如2019-01-15T10:59:19.4102019-01-15 10:59:19.410 不同。在 DMY 环境(英国)中,第二种格式失败,因为 15 是第 15 个月的 SQL 事物。它在美国(us_english)MDY 环境中确实有效。问题是我很困惑为什么 EF 似乎以2019-01-15 10:59:19.410 格式生成日期并在从前端运行时在 DMY 环境中成功执行,但从探查器复制的相同 SQL 不会运行(如预期的那样)。跨度>
  • Jeroen - 这是有道理的。我已经开始考虑 Profiler 可能没有向我显示执行的确切 SQL 语句,并且可能正在格式化日期字符串以用于显示目的。本质上,分析器可能正在执行类似SELECT CONVERT(VARCHAR, @SomeDate, 21) 的操作来显示语句。

标签: sql-server asp.net-core entity-framework-core


【解决方案1】:

实际发生的是,Entity Framework 生成参数化语句,然后使用(二进制)TDS 协议将其传递给服务器,这就是应用程序与 SQL Server 对话的方式。 DATETIME 作为 RPC 参数传递的值不会作为字符串传递——它们作为代表精确值的字节序列传递(例如,2019-01-15T10:59:19.410 在作为 DATETIME 发送时表示为 D6 A9 00 00 AF 16 B5 00(两个小-endian 整数)。(免责声明:我实际上没有使用数据包嗅探器进行检查,所以我可能弄错了字节)。

当 SQL Profiler 将这些参数化语句呈现为文本批处理时,它会将值重新格式化为字符串,因为 T-SQL 没有 DATETIME 值的本机文字格式。不幸的是,它使用的格式不能保证在所有可能的语言设置下都可以往返,这应该被视为 Profiler 中的一个错误。它只缺少一个T,但这是一个重要的遗漏。

类似的事情发生在神秘的exec sp_reset_connection 调用中,您可以看到所有使用连接池的应用程序的痕迹都乱七八糟——实际上没有应用程序生成这些调用,如果您尝试在 Management Studio 中执行此语句,您会看到没有sp_reset_connection 程序。在协议级别,数据包标头中只有一个“重置此连接”位,该位被转换为一个虚构的调用,其中虚构的 SET 语句表示当前选项,当它作为语句或事件跟踪呈现时。

简而言之:您的应用程序运行良好,但如果您想准确回放调用时发生的情况,则无法复制粘贴 Profiler 输出。

【讨论】:

  • Joroen - 这就是答案。不幸的是,我还不能投票,因为我需要 15 个声望。一旦我的声誉达到门槛,我会回来投票。谢谢!
  • @RobinWebb:没有必要投票 - 如果这回答了问题,您可以使用分数下方的复选框将其标记为已接受,这不需要任何声誉。
  • 我刚刚偶然发现了这个解释得很清楚的博客 - weblogs.sqlteam.com/dang/2007/10/11/…
  • @RobinWebb:我尝试的每个浏览器都损坏了该帖子中的图像,这严重限制了实用性——而archive.org没有。你有我没有看到的完整版本吗?
  • 不幸的是,这个(相当旧的)博客中的图像被破坏了 - weblogs.sqlteam.com/dang/2007/10/11/…。但是,幸存文本中提供的信息最有用,并进一步阐明了您的原始答案。
【解决方案2】:

我刚刚偶然发现了这个解释得很清楚的博客 - https://weblogs.sqlteam.com/dang/2007/10/11/sql-trace-parameter-values-are-not-always-as-they-seem/

以上链接中的图片不再可用(最初发布于 2007 年)。

然而,文本内容非常丰富,我在它完全消失之前将其粘贴在下面。

我希望这对某人有所帮助。

丹·古兹曼博客

SQL 跟踪参数值并不总是看起来像 Dan Guzman 2007 年 10 月 11 日星期四

我偶然发现了我认为令人惊讶的 SQL Trace/Profiler 行为 值得一提。跟踪 RPC 中报告的参数值 开始/完成事件不是 SQL Server 使用的值 执行查询。这是我最近发现的一个例子 这种行为。我运行以下 C# 代码来执行参数化 查询“SELECT @DateTime”,参数值设置为 10 月 11 日, 2007. 控制台消息验证 SQL Server 返回了预期的日期。

我从跟踪中粘贴脚本并从 SQL Server 运行它 Management Studio 查询窗口。这是 SSMS 脚本的踪迹 执行:

尽管 SQL 看起来相同,但 SSMS 查询返回了一个 与申请(“10 月”)不同的日期(“2007 年 11 月 10 日”) 11, 2007")!这是怎么回事?为什么会返回相同的脚本 从应用程序代码与 Management Studio 运行时的不同值 (或查询分析器),即使具有相同的会话设置?

行为差异的原因是跟踪事件类。这 应用程序跟踪显示 RPC:Completed 事件但 SSMS 会话 跟踪显示 SQL:BatchCompleted。两者都有相同的“执行 sp_executesql…”TextData 中的语句。尽管有痕迹 为这两个事件报告了相同的 TextData,SQL Server 处理了 RPC 与 SQL 批处理不同,并且跟踪没有显示 RPC 事件的完整故事。

问题在于 RPC 事件的日期时间输入参数值 并没有像跟踪那样真正包含在 SQL 语句中 RPC:显示已完成事件。实际的 datetime 参数值 在 RPC 执行期间使用的 SQL Server 以本机方式传递(即 二进制)格式通过低级 TDS 协议。跟踪 TextData 是 只是该值的反向工程字符串表示,而不是 比 SQL Server 使用的实际值。

相比之下,SSMS 发送的批处理没有参数化,因此 SQL Server 需要解析批处理文本中的日期时间字符串值。这 日期时间字符串“2007-10-11 00:00:00:000”(由跟踪生成,而不是 由应用程序传递)是模棱两可的,并且解释不同,具体取决于 在 DATEFORMAT 设置上。由于“DATEFORMAT DMY”设置在 脚本,日期字符串值被(错误)解释为 11 月 10 日, 2007 而不是 2007 年 10 月 11 日。DATEFORMAT 设置没有 影响 RPC(参数化值),因为设置会影响 只有字符串解析,而不是由 应用。

我在帮助正在使用的用户时发现了这个问题 法语 SQL Server(这就是我指定 DATEFORMAT DMY 的原因) 解决日期时间参数问题。跟踪数据使我失望 错误的路径,因为它没有立即点击我 1) DATEFORMAT 不影响 RPC 日期时间参数和 2) 跟踪 RPC TextData 无论如何都不用于查询执行。我提交了连接 对此的反馈建议将 SQL Trace/Profiler 更改为 以 DATEFORMAT 中性格式序列化 RPC 日期时间值,例如 “2007-10-11T00:00:00:000”或“20071011 00:00:00:000”。这会 当跟踪 TextData SQL 作为 批处理并在分析跟踪数据时更加直观。

版权所有 © 2018 - Dan Guzman

【讨论】:

  • 请不要发link-only answer。 SO的答案应该是自包含的,以防链接失效,因此您至少需要提供简短摘要或相关内容的摘录。据我所知,丹只是在说我也说过的话,所以您也可以在我的答案中的链接中进行编辑,或者将其作为评论留下——我会接受该编辑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-24
  • 1970-01-01
  • 2020-08-15
相关资源
最近更新 更多