C#拼接SQL按日期筛选报字符串转日期/时间失败异常如何修复
异常根因
你遇到的日期转换异常是直接拼接DateTime值到SQL字符串导致的:
- C#中将
dtFrom.Value、dtTo.Value直接拼入字符串时,会默认使用当前操作系统的区域设置格式化日期,可能生成2024/5/20、5/20/2024、2024年5月20日等不同格式的字符串 - SQL Server服务端解析日期字符串时使用自身的区域配置,一旦和客户端拼接出的格式不匹配,就会抛出"Conversion failed when converting date and/or time from character string"异常
- 除此之外,字符串拼接SQL还存在SQL注入风险,原代码的日期范围逻辑也有漏洞:DateTimePicker默认带当前时分秒,若结束日期未手动选到23:59:59,会漏掉结束日当天的大部分数据。
修复方案
核心是改用参数化查询,完全跳过日期转字符串的环节,同时修正日期范围逻辑、补上资源释放处理,修复后完整代码如下:
public void LoadSold() { int i = 0; double total = 0; dgvSold.Rows.Clear(); // 抽取公共SQL片段,减少重复代码 string querySql = @"SELECT c.CartID, c.transno, c.ProductID, p.ProdDescription, c.price, c.qty, c.disc, c.total FROM tbCart AS c INNER JOIN Product AS p ON c.ProductID = p.ProductID WHERE status = 'Sold' AND sdate >= @StartDate AND sdate < @EndDate"; // 非全量查询场景追加收银员筛选条件 if (cboSales.Text != "All Sales") { querySql += " AND cashier LIKE @Cashier"; } // using块自动释放数据库资源,避免异常时连接泄漏 using (con) { con.Open(); using (SqlCommand command = new SqlCommand(querySql, con)) { // 日期参数直接传DateTime类型,无需手动转字符串,彻底规避格式不匹配问题 command.Parameters.Add("@StartDate", SqlDbType.DateTime).Value = dtFrom.Value.Date; // 结束时间取选中日期次日0点,覆盖结束日全天所有数据,比BETWEEN逻辑更严谨 command.Parameters.Add("@EndDate", SqlDbType.DateTime).Value = dtTo.Value.Date.AddDays(1); if (cboSales.Text != "All Sales") { command.Parameters.Add("@Cashier", SqlDbType.NVarChar).Value = $"%{cboSales.Text}%"; } using (SqlDataReader dr = command.ExecuteReader()) { while (dr.Read()) { i++; total += Convert.ToDouble(dr["total"]); dgvSold.Rows.Add(i, dr[0].ToString(), dr[1].ToString(), dr[2].ToString(), dr[3].ToString(), dr[4].ToString(), dr[5].ToString(), dr[6].ToString(), dr[7].ToString()); } } } con.Close(); } lblTotal.Text = total.ToString("#,##0.00"); }
关键改动说明
- 所有动态传入SQL的值都通过
SqlParameter传递,日期类型直接传结构体值,ADO.NET会自动处理和SQL Server的类型匹配,完全不会再出现字符串转日期的格式错误,同时彻底堵上SQL注入漏洞。 - 日期范围改用
>= 起始日0点、< 结束日次日0点的判断逻辑,不需要手动调整时分秒就能覆盖选中时段的全部数据,避免BETWEEN的边界遗漏问题。 - 原代码中
status LIKE 'Sold'没有通配符,等价于等值判断,直接改为status = 'Sold'执行效率更高。 - 增加
using块包裹连接、命令、数据读取器对象,即使执行过程中抛出异常也能自动释放非托管资源,不会出现连接池占满的问题。
内容的提问来源于stack exchange,提问作者yannis
相关产品推荐
相关产品推荐

