【问题标题】:Proper Way To Use .Net DateTime in Parameterized SQL String在参数化 SQL 字符串中使用 .Net DateTime 的正确方法
【发布时间】:2012-05-05 12:25:29
【问题描述】:

关于在 Sql 语句中使用 .Net DateTime 有几个问题,但没有一个能解决我的问题。

我正在使用以下代码查询 Oracle 数据库:

private DataTable QueryByIdAndDate(string id, DateTime fdate, DateTime tdate) {
    string query = "SELECT * FROM table WHERE ID = :id AND DATE_TIME BETWEEN :from AND :to"
    DbCommand cmd = db.CreateCommand();
    cmd.CommandType = CommandType.Text;
    cmd.CommandText = query;

    DbParameter fromDate = CreateDateParameter(cmd, fdate);
    fromDate.ParameterName = "from";
    DbParameter toDate = CreateDateParameter(cmd, tdate);
    toDate.ParameterName = "to"; 
    DbParameter idParam = CreateStringParameter(cmd, id);
    idParam.ParameterName = "id";

    cmd.Parameters.AddRange(new DbParameter[] { fromDate, toDate, idParam });
    return db.ExecuteQuery(cmd);
}

private DbParameter CreateDateParameter(DbCommand cmd, DateTime date) {
    DbParameter param = cmd.CreateParameter();
    param.DbType = DbType.DateTime;
    param.Direction = ParameterDirection.Input;
    param.Value = date;
    return param;
}

但它不能正常工作。像这样尝试代码时:

DataTable result = QueryByIdAndDate("12345", DateTime.Now, DateTime.Now.AddDays(1));

它给出了以下错误: ORA-01847: 日期必须介于 1 和最后一天之间

我假设它与 DateTime 的格式化方式有关,但我不知道以可靠方式解决此问题的正确方法。

【问题讨论】:

  • 你不会碰巧在一个月的第 30 天运行它,只有 30 天吗?
  • 不。我使用 DateTime.Now 和 DateTime.Now.AddDays(1); 展示的示例失败了
  • 你的驱动肯定支持命名参数吗?如果它试图按位置使用它们,它最终会使用“id”值作为“to”部分,这可能会被破坏。您使用的是哪个驱动程序?
  • @JonSkeet 哇,你猜对了。我正在使用 Oracle.DataAccess,它必须按位置使用它们,因为我在将参数放入数组时更改了参数的顺序,现在它正在工作。搞砸了一个小时后,这是多么令人讨厌的结果。把它作为答案,你就赢了,谢谢!
  • @user12345613:很高兴看到心理调试经常起作用:)

标签: c# .net sql oracle datetime


【解决方案1】:

对于那些可能会错过它的人,请参阅 @JonSkeetthe accepted answer@kprobst 的评论:

如果您尝试使用名称绑定而不在Command 上设置BindByName 属性,那么设置参数值等的格式并不重要。

(整个 Oracle 提供程序 API 就是一堆垃圾恕我直言)。

【讨论】:

    【解决方案2】:

    DateTime 值转换为格式化的日期字符串:

    private DbParameter CreateDateParameter(DbCommand cmd, DateTime date) 
    {
         DbParameter param = cmd.CreateParameter();
         param.DbType = DbType.DateTime;
         param.Direction = ParameterDirection.Input;
         param.Value = date.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
         return param;
    } 
    

    即使您发送的是字符串值,DbType 仍设置为DateTime,因此该值应正确转换。

    【讨论】:

    • 请注意,即使这确实是问题所在,您也可能希望使用不变的文化,并且您肯定希望将“hh”更改为“HH”。不过,我强烈建议反对自己执行此类转换。
    • @JonSkeet 什么也得不到。同意,我直接跳到格式问题,从未考虑过参数顺序。根据您的建议更新。
    【解决方案3】:

    (根据 cmets...)

    看起来在这种情况下,参数的顺序很重要……尽管您已经给它们起了名字。我没想到会这样,这是驱动程序有些损坏的迹象,但是将您的代码更改为:

    cmd.Parameters.AddRange(new DbParameter[] { idParam, fromDate, toDate });
    

    应该修复它。 (顺便说一下,这不一定是您构建参数的方式,但这在这里有些无关紧要。)

    开始将日期/时间值指定为字符串。引入比您需要的更多的字符串转换是一个非常糟糕的主意

    【讨论】:

    • 是的,我什至没有考虑该数组的顺序(这很愚蠢),因为它们是命名参数。另外,我愿意接受任何有关正确构造参数的 cmets 或建议。
    • 根据我的经验,oracle .net 驱动程序中存在很多问题,包括参数顺序或需要更改 SQL 语句以消除 NULL 引用异常。
    • @user12345613:我不能说这是一个我有很多经验的领域,但如果你使用强类型命令参数集合,你可能会得到诸如AddWithValue 这样的选项让生活更轻松。
    • @JonSkeet 我得去看看。就目前而言,我很高兴这能奏效。
    • ODP.Net 中的 OracleCommand 对象有一个名为 BindByName 的属性,当设置为 true 时可以消除此问题。默认设置为 false,因为以前版本的驱动程序用于按位置绑定。
    【解决方案4】:

    Oracle 期望日期格式为“DD-Mon-YYYY”,因此您需要基本上以这种方式格式化您的日期值并将其分配给参数值。

    编辑:我注意到最新版本的 ODP 似乎可以更好地处理日期时间参数。例如,当 WHERE 子句在日期/时间列上存在约束时,这可以正常工作:

    DateTime dt = new DateTime(2012, 5, 21);
    cmd.Parameters.Add("some_date_param", dt);
    

    以这种方式使用最新版本的 ODP 运行查询可以正常工作。但是我有很多代码需要将日期/时间值作为格式化字符串传递,以便 Oracle 接受它们。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-11
      • 1970-01-01
      • 1970-01-01
      • 2016-06-28
      相关资源
      最近更新 更多