我们有一个使用英语(美国)安装的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
让我感到困惑的是,即使环境设置为英式英语(DMY),尽管日期以yyyy-mm-dd
格式存在,该语句仍能成功运行。例如-"2019-01-15 10:59:19.410"
。
我了解到,如果DMY是当前设置,则“ 2019-01-15 10:59:19.410”将被解释为2019年15月15日的第一天。
例如,这将失败,因为SQL会尝试将日期字符串解释为yyyy-dd-mm
SET LANGUAGE [British English];
SELECT
CAST('2019-01-13' AS DATETIME);
消息242,级别16,状态3,第3行varchar数据的转换 类型转换为日期时间数据类型会导致值超出范围。
将语言设置为英语(us_english)会将字符串解释更改为MDY,并且上面的语句起作用。
我知道我可以使用不明确的日期字符串,但是我遇到的问题与EF前端生成的日期字符串有关。
我不知道为什么该语句成功运行。显然,存在从日期字符串到DATETIME类型的隐式强制转换。
如果我从SQL事件探查器复制该语句并将其粘贴到SSMS中,并尝试针对同一数据库/相同用户运行,则它将失败,如我所愿。
消息8114,级别16,状态1,第14行转换数据类型时出错 varchar转换为datetime。
所以...
我了解从字符串日期转换为DATETIME类型时发生了什么,并在SSMS中运行查询获得了一致的结果,但是我不明白为什么从前端运行SSMS中失败的查询的原因。
如果对此没有很好的解释,我深表歉意。
答案 0 :(得分:4)
实际发生的情况是,Entity Framework生成参数化的语句,然后使用(二进制)TDS协议将其传递到服务器,这是应用程序与SQL Server进行通信的方式。作为RPC参数传递的DATETIME
值不作为字符串传递-它们作为表示精确值的字节序列传递(例如,当2019-01-15T10:59:19.410
作为a发送时,表示为D6 A9 00 00 AF 16 B5 00
DATETIME
(两个小尾数整数)(免责声明:我实际上没有用数据包嗅探器检查过,因此我可能弄错了字节)。
当SQL Profiler将这些参数化语句视为文本批处理时,由于T-SQL没有DATETIME
值的本机文本格式,因此将值重新格式化为字符串。不幸的是,它使用的格式不能保证在所有可能的语言设置下都可以往返,这在Profiler中应视为错误。它只缺少T
,但这是一个重要的遗漏。
神秘的exec sp_reset_connection
调用也会发生类似的情况,您会看到乱七八糟的所有使用连接池的应用程序的踪迹-没有应用程序实际生成这些调用,如果您尝试在Management Studio中执行此语句,您会将会看到没有sp_reset_connection
过程。在协议级别上,数据包头中只有一个“重置此连接”位,当将其表示为语句或事件跟踪时,该位将转换为包含代表当前选项的虚拟SET
语句的虚拟调用。 / p>
简而言之:您的应用程序运行良好,但是如果要准确重放它们在进行调用时发生的情况,则复制粘贴Profiler输出将无效。
答案 1 :(得分:0)
我偶然发现了一个非常清晰的博客-https://weblogs.sqlteam.com/dang/2007/10/11/sql-trace-parameter-values-are-not-always-as-they-seem/
以上链接中的图像不再可用(最初于2007年发布)。
但是该文本非常有用,在完全消失之前,我在下面粘贴了该文本。
我希望这对某人有帮助。
Dan Guzman博客
SQL跟踪参数值并不总是看起来像Dan Guzman在2007年10月11日星期四那样
我偶然发现了我认为是令人惊讶的SQL Trace / Profiler行为 值得一提。跟踪RPC中报告的参数值 开始/完成事件不是SQL Server用于执行以下操作的值 执行查询。这是我最近发现的一个示例,该示例显示 这种行为。我运行以下C#代码以执行参数化 查询参数值设置为10月11日的“ SELECT @DateTime” 2007。控制台消息验证SQL Server返回了预期的日期。
我从跟踪中粘贴了脚本并从SQL Server中运行了该脚本 Management Studio查询窗口。这是SSMS脚本的痕迹 执行:
即使SQL看起来相同,SSMS查询也会返回一个 与申请日期(“十月”)不同的日期(“ 2007年11月10日”) 2007年11月11日”)!这是怎么回事?为什么相同的脚本会返回 从应用程序代码与Management Studio运行时,它们的值不同 (或查询分析器),甚至具有相同的会话设置?
行为差异的原因是跟踪事件类。的 应用程序跟踪显示了RPC:Completed事件,但SSMS会话 跟踪显示SQL:BatchCompleted。两者都有相同的“执行 sp_executesql…”语句在TextData中。即使痕迹 为两个事件报告了相同的TextData,SQL Server处理了 RPC与SQL批处理不同,跟踪没有显示 RPC事件的完整故事。
问题是RPC事件的datetime输入参数值 并没有像跟踪那样真正包含在SQL语句中 显示RPC:已完成事件。实际的datetime参数值 在RPC执行期间使用的SQL Server是通过本机传递的(即 二进制)格式通过低级TDS协议。跟踪TextData是 只是对该值进行反向工程的字符串表示,而不是 比实际使用的SQL Server值高。
相比之下,SSMS发送的批处理没有参数化,因此SQL Server 需要解析批处理文本中的datetime字符串值。的 datetime字符串“ 2007-10-11 00:00:00:000”(由跟踪生成,不是 由应用程序传递)是模棱两可的,并根据情况进行了不同的解释 在DATEFORMAT设置上。由于设置了“ DATEFORMAT DMY” 脚本,日期字符串值被(错误)解释为11月10日, 2007年而不是2007年10月11日。DATEFORMAT设置没有 因为设置会影响RPC(参数化值) 仅字符串解析,而不是 应用。
我在帮助正在使用的用户时发现了此问题 法语SQL Server(这就是为什么我指定DATEFORMAT DMY的原因) 解决datetime参数问题。跟踪数据使我失望 错误的路径,因为它没有立即与我点击1) DATEFORMAT不会影响RPC日期时间参数,并且2)跟踪RPC 无论如何,TextData都不用于查询执行。我提交了Connect 对此的反馈,建议将SQL跟踪/配置器更改为 以DATEFORMAT中性格式对RPC日期时间值进行序列化,例如 “ 2007-10-11T00:00:00:000”或“ 20071011 00:00:00:000”。这将 当跟踪TextData SQL执行为 批处理,并且在分析跟踪数据时更加直观。
版权所有©2018-Dan Guzman