【问题标题】:C# Potential Date format issue - System.Data.SqlClient.SqlException: 'Incorrect syntax near 'x'.'C# 潜在的日期格式问题 - System.Data.SqlClient.SqlException:“'x' 附近的语法不正确。”
【发布时间】:2018-07-18 09:24:46
【问题描述】:

应用程序的目的是从Sql 数据库中选择数据到windows 窗体数据网格。我为日期时间选择器实现了自定义格式,但仍然收到此错误。这是应用程序的属性和以下代码

Date Time Picker Properties

        private void startTimePicker1_MouseDown(object sender, MouseEventArgs e)
    {
        startTimePicker1.CustomFormat = "yyyy-MM-dd hh:mm:ss";
        startTimePicker1.Format = DateTimePickerFormat.Custom;
    }

    private void endTimePicker1_MouseDown(object sender, MouseEventArgs e)
    {
        endTimePicker1.CustomFormat = "yyyy-MM-dd hh:mm:ss";
        endTimePicker1.Format = DateTimePickerFormat.Custom;
    }

    private void loadBtn_Click(object sender, EventArgs e)
    {
        startTimePicker1.CustomFormat = "yyyy-MM-dd hh:mm:ss";
        startTimePicker1.Format = DateTimePickerFormat.Custom;

        endTimePicker1.CustomFormat = "yyyy-MM-dd hh:mm:ss";
        endTimePicker1.Format = DateTimePickerFormat.Custom;

        using (IDbConnection db = new SqlConnection(ConfigurationManager.ConnectionStrings["DBName"].ConnectionString))
        {
            if(db.State==ConnectionState.Closed)
            {
                db.Open();
                string query = "select z.ItemName, x.Quantity, x.CreateDate" +
                                 " from Invoice_Itemized x inner join Inventory z on x.ItemNum = z.ItemNum inner join Departments d on z.Dept_ID = d.Dept_ID" +
                                $"where x.CreateDate BETWEEN '{ startTimePicker1 }' and '{ endTimePicker1 }' and d.SubType = 'TYPE'";

                liquorBindingSource.DataSource = db.Query<Liquor>(query, commandType: CommandType.Text);
            }
        }

查询在运行时输出以下值

select z.ItemName, x.Quantity, x.CreateDate from Invoice_Itemized x inner join Inventory z on x.ItemNum = z.ItemNum inner join Departments d on z.Dept_ID = d.Dept_IDwhere x.CreateDate BETWEEN 'System.Windows.Forms.DateTimePicker, Value: 7/18/2018 2:44:00 AM' and 'System.Windows.Forms.DateTimePicker, Value: 7/18/2018 4:19:01 AM' and d.SubType = 'TYPE'

这个查询在 SSMS 中成功执行,经过几个小时的调试,我遇到了巨大的障碍。

【问题讨论】:

  • 使用 SQL 变量,不要字符串化你的日期
  • on z.Dept_ID = d.Dept_IDwhere x.CreateDate'System.Windows.Forms.DateTimePicker, Value: 7/18/2018 2:44:00 AM' 缺少空格不是有效的 SQL 日期
  • 您也可以删除您的if 测试。除了通过调用IDbConnection db = new SqlConnection(... 关闭连接之外,您收到其他内容的可能性为0。

标签: c# sql


【解决方案1】:
string query = "select z.ItemName, x.Quantity, x.CreateDate" +
      " from Invoice_Itemized x inner join Inventory z on x.ItemNum = z.ItemNum inner join Departments d on z.Dept_ID = d.Dept_ID" +
      $"where x.CreateDate BETWEEN '{ startTimePicker1 }' and '{ endTimePicker1 }' and d.SubType = 'TYPE'";

liquorBindingSource.DataSource = db.Query<Liquor>(query, commandType: CommandType.Text);

这是难以置信有害的,并且可能导致多个问题,包括格式/文化问题(它在某些机器上工作,但在其他机器上工作,取决于区域设置) - 和(更重要的是)SQL注入风险。它也不允许查询计划缓存重用。

由于您在此处使用“dapper”,因此 设计 dapper 使其易于参数化:

liquorBindingSource.DataSource = db.Query<Liquor>(@"
        select z.ItemName, x.Quantity, x.CreateDate
        from Invoice_Itemized x
        inner join Inventory z on x.ItemNum = z.ItemNum
        inner join Departments d on z.Dept_ID = d.Dept_ID
        where x.CreateDate BETWEEN @start and @end and d.SubType = 'TYPE'",
    new { start = startTimePicker1.Value, end = endTimePicker1.Value });

这会将值作为类型化参数传递(假设这里的.ValueDateTime),避免了所有本地问题并且消除了SQL 注入的风险。如果.Value 不是 DateTime,而只是string,那么我会使用:

new { start = DateTime.Parse(startTimePicker1.Value),
        end = DateTime.Parse(endTimePicker1.Value) }

作为第二个参数。

【讨论】:

  • 如果你打算把它当作 Dapper,把它当作 WinForms DateTimePicker。 :-) 控件属性的屏幕截图看起来很匹配,其他事件处理程序也建议使用 WinForms。
  • 谢谢马克!这解决了我遇到的问题
【解决方案2】:

这里有两个不同的问题。

一个是您将 DateTimePicker 的引用传递到字符串插值中,导致字符串包含从 DateTimePicker.ToString() 返回的值,而不是您想要的值。

第二个问题是,您首先不应该将日期作为字符串传递给数据库。相反,您应该使用 SQL 参数来传递 DateTime 的实例。我不确定你使用的是什么 ORM,所以我不能给你一个代码示例。

此外,正如 fubo 在对问题的评论中所写,Where 关键字之前缺少一个空格。

【讨论】:

  • 看起来很像 Dapper 但你是对的,在 OP 澄清之前,不可能给出具体的更正。他们还需要修复 fubo 在 cmets 中发现的空间缺失问题。
  • @Damien_The_Unbeliever 我也这么认为,但我了解到,假设有关问题的事情通常会导致错误的答案。之前甚至没有看到缺少的空间,我也会将其编辑到我的答案中,谢谢!。
  • 我正在使用 Dapper。这是我声明的属性, public class Liquor { public string ItemName { get;放; } 公共小数 ItemNum { 获取;放; } 公共日期时间 CreateDate { 获取;放; } }
  • 在这种情况下,请选择 Marc 的答案。
  • 它们都用在同一列上没关系,它们是不同的参数……我个人喜欢存储过程。我一直都在使用它们——但对于这样的查询来说应该不会有太大的不同。
猜你喜欢
  • 2021-03-31
  • 2019-07-17
  • 2017-12-08
  • 2014-05-09
  • 2018-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多