【问题标题】:SQLClient running Varchar conversions on Datetimes and having errorsSQLClient 在 Datetimes 上运行 Varchar 转换并出现错误
【发布时间】:2021-12-16 06:37:00
【问题描述】:

我正在尝试运行 UPDATE 命令,该命令将 DateTime 数据类型存储在 SQL Server 表中,但是每当它到达该行时

command.ExecuteNonQuery();

程序因以下错误而失败:

将 varchar 数据类型转换为 datetime 数据类型导致值超出范围

我已经设法收集到这可能意味着我正在使用的 DateTime 格式,但无论我试图将时间转换成什么形状,它仍然会吐出完全相同的错误。

数据存储格式为en-GB,读取如下:

StartDateTime = DateTime.Parse(items[0], new CultureInfo("en-GB"));

当我尝试在 SQL 中存储时间时,我当前使用的是以下格式:

const string ParamStart = "@NewStartDate";
command.Parameters.Add(ParamStart, System.Data.SqlDbType.DateTime).Value = newStartDate;

存储在 SQL Server 表中的数据以这种格式存储:

yyyy-MM-dd HH:mm:ss.fff

我的 SQL 阅读器从 SQL Server 获取数据没有问题,但是一旦我开始使用参数,它就会报错。

真正令人沮丧的是,在同一个文件的其他地方我有一个SELECT 命令,它使用完全相同的格式将数据包含在查询中:

command.Parameters.Add(ParamStart, System.Data.SqlDbType.DateTime).Value = startDate;

这很好用。

SQL Server 中该列的数据类型是datetime,不能为空。

出现在我的 C# 代码中的查询:

string query = $"UPDATE [{DB_NAME}].[dbo].[{TABLE_BATCH}] SET [{COLUMN_START}] = {ParamStart}, [{COLUMN_MACHINE}] = {ParamMachine}," +
                $" [{COLUMN_SERIAL}] = {ParamSerial}, {(newBatch.EndDateTime != null ? $"[{COLUMN_END}] = {ParamEnd}, " : "")}[{COLUMN_COMPLETE}] = {ParamComplete}, [{COLUMN_IN_USE}] = {ParamInUse} {oldBatch.ToWhere()}";

COLUMN_Param 开头的任何内容都只是一个仅包含字符串的常量。使用它是为了避免拼写错误,并且可以在列名更改时编辑代码。

ToWhere() 方法从预先从数据库中提取的现有项目创建WHEREWHERE 使用插值字符串,因此不应在此处进行参数转换,但无论如何我都会发布 return 子句的格式:

$"WHERE [StartDateTime] = '{StartDateTime}' AND [MachineID] = '{MachineID}' AND [BatchNumber] = '{BatchNumber}'" +
                $" AND [SAPOrderNumber] {sapString} AND [PartNumber] = '{PartNumber}' AND [LastSerialNumber] = {LastSerialNumber}" +
                $" AND [EndDateTime] {endDateString} AND [Complete] {completeString} AND [BatchInUse] {inUseString}";

我需要对格式进行哪些更改才能使 SQL Server 识别日期?

【问题讨论】:

  • 日期是二进制值,如 int 和 double,它们没有格式。不要将日期存储为字符串。使用正确的类型 - datedatetime2datetimeoffset。如果您将日期存储为字符串,那么您将遇到严重的错误,并且很可能是无法挽救的垃圾数据。根本无法确定07/04/2021 是 4 月 7 日还是 7 月 4 日
  • 在 C# 中使用 DateTime,而不是字符串。
  • 我将它存储为日期时间。
  • 如果你这样做,varchar 来自哪里?查询有什么作用?发布实际重现问题的代码。不知何故,在某处,您将日期作为文本处理
  • 参数不是问题,无论您尝试执行什么查询。当DATETIMEVARCHAR 值组合在一起时,SQL Server 将始终倾向于将VARCHAR 转换为DATETIME 以进行比较,而不是相反。因此,如果 any VARCHAR 值未使用服务器的语言设置隐式转换为 DATETIME,则会出现此错误。解决方案是修复查询,使其使用显式转换(或一劳永逸地修复数据,这样就不会发生类型的混合和匹配)。

标签: c# sql-server sqlclient


【解决方案1】:

问题出在我的 WHERE 子句中,插值字符串中的日期时间格式不一定正确,直到更新此问题后我才发现。

【讨论】:

  • 日期没有格式。故事结局。 不要使用插值字符串,使用参数。使用内插字符串 的错误。这使您对 SQL 注入攻击和您遇到的转换错误敞开大门。如果你改用参数,就不会有问题
  • $" AND [EndDateTime] {endDateString} 这应该做什么?事实上,它只是无效的 SQL。您是否尝试将运算符与值一起传递?
  • @PanagiotisKanavos 就像我在问题中所说的那样,该插值字符串中的数据仅来自服务器本身。在这种情况下,用户输入为零,我认为这使得这种情况对 SQL 注入非常安全。此外,它还使用了“IS NULL”或“='EndDate'”的检查,这已被删除以使用参数化方法。
  • 你仍然有一个错误。因为 client 对这些字符串的格式与服务器不同。此外,代码仍然对 SQL 注入开放。数据来自服务器,但如果这些数据一开始就被污染了怎么办?如果sapString 包含=''; Drop Table Users;-- 怎么办?
  • 我不完全确定这种不良数据如何在不执行的情况下进入数据库。但我确实明白你的意思。假设整数和位数据可以安全插值是否安全?
猜你喜欢
  • 2016-09-08
  • 1970-01-01
  • 2016-10-30
  • 2012-08-16
  • 1970-01-01
  • 2016-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多