【问题标题】:TO_DATE() function gives an error when date format variable is 'M/d/yy' and month or date is a two digit value当日期格式变量为“M/d/yy”且月份或日期为两位数时,TO_DATE() 函数会出错
【发布时间】:2018-08-15 11:53:38
【问题描述】:

我的日期格式因用户的日期格式而异。 当日期格式为 'M/d/yy' 时,会报错,ORA-01821:日期格式无法识别。

sDateFormat = CultureInfo.DateTimeFormat.ShortDatePattern;

SELECT * FROM xxx WHERE yyy <= TO_DATE('" + sScheduleDate + "','"+sDateFormat+"')";

//sScheduleDate 例如:“11/15/18” //sDateFormat "M/d/yy"

【问题讨论】:

    标签: c# sql oracle to-date


    【解决方案1】:

    我看到两个问题:

    1. 就像 HoTTab1CH 所说:如果构建查询,您应该始终使用 OracleParameters (https://en.wikipedia.org/wiki/SQL_injection)

    2. 您正在为 Oracle 使用 C# 模式。这可能有效(不变文化)机器人通常不会(分钟)。

    .

    // This will get you the Pattern "MM/dd/yyyy"
    string invariantPattern = CultureInfo.InvariantCulture.DateTimeFormat.ShortDatePattern;
    
    // This will get me in a German-Environment the Pattern "dd.MM.yyyy"
    string invariantPattern = CultureInfo.InvariantCulture.DateTimeFormat.ShortDatePattern;
    

    这两种模式都与 Oracle 无关。他们可能会工作,但你不知道。不得在 .Net 环境之外使用它们!

    什么是允许的:

    OracleCommand cmd = null; // You should have this one already initialized..
    
    // Your Idea:
    DateTime date = new DateTime(2018, 12, 31, 23, 59, 59);
    string csharpPattern = "dd.MM.yyyy HH:mm:ss";
    string oraclePattern = "dd.mm.yyyy HH24:MI:SS";
    string toDateQuery = "to_date('" + date.ToString(csharpPattern) + "','" + oraclePattern + "')";
    string sqlQuery = "SELECT * FROM mytable t WHERE t.mydate = " + toDateQuery;
    cmd.CommandText = sqlQuery;
    var reader = cmd.ExecuteReader();
    // Do something...
    
    // But... Better, shorter and correcter(?)
    DateTime date2 = new DateTime(2018, 12, 31, 23, 59, 59);
    cmd.CommandText = "SELECT * FROM mytable t WHERE t.mydate = :MYDATE";
    cmd.Parameters.Add(new OracleParameter(":MYDATE", date2));
    

    补充说明

    表格

    CREATE TABLE TEST
    (
      TESTDATE               DATE,
      TESTTIMESTAMP          TIMESTAMP(6),
      TESTTIMESTAMPTIMEZONE  TIMESTAMP(6) WITH TIME ZONE
    )
    

    C#-应用程序

    DateTime d = DateTime.Now;
    
    // Let OPD.Net do the work..
    OracleCommand  cmd = con.CreateCommand();
    cmd.CommandText = "INSERT INTO TEST VALUES(:TESTDATE, :TESTTIMESTAMP, :TESTTIMESTAMPTIMEZONE)";
    cmd.Parameters.Add(new OracleParameter("TESTDATE", d));
    cmd.Parameters.Add(new OracleParameter("TESTTIMESTAMP", d));
    cmd.Parameters.Add(new OracleParameter("TESTTIMESTAMPTIMEZONE", d));
    cmd.ExecuteNonQuery();
    
    // Try to manually hit the OracleTypes - and loose the milliseconds..
    cmd = con.CreateCommand();
    cmd.CommandText = "INSERT INTO TEST VALUES(:TESTDATE, :TESTTIMESTAMP, :TESTTIMESTAMPTIMEZONE)";
    cmd.Parameters.Add(new OracleParameter("TESTDATE", OracleDbType.Date, d, System.Data.ParameterDirection.Input));
    cmd.Parameters.Add(new OracleParameter("TESTTIMESTAMP", OracleDbType.Date, d, System.Data.ParameterDirection.Input));
    cmd.Parameters.Add(new OracleParameter("TESTTIMESTAMPTIMEZONE", OracleDbType.Date, d, System.Data.ParameterDirection.Input));
    cmd.ExecuteNonQuery();
    
    // Set everything correct (and redundant..)
    cmd = con.CreateCommand();
    cmd.CommandText = "INSERT INTO TEST VALUES(:TESTDATE, :TESTTIMESTAMP, :TESTTIMESTAMPTIMEZONE)";
    cmd.Parameters.Add(new OracleParameter("TESTDATE", OracleDbType.Date, d, System.Data.ParameterDirection.Input));
    cmd.Parameters.Add(new OracleParameter("TESTTIMESTAMP", OracleDbType.TimeStamp, d, System.Data.ParameterDirection.Input));
    cmd.Parameters.Add(new OracleParameter("TESTTIMESTAMPTIMEZONE", OracleDbType.TimeStampTZ, d, System.Data.ParameterDirection.Input));
    cmd.ExecuteNonQuery();
    

    数据库数据

    | TESTDATE            | TESTTIMESTAMP              | TESTTIMESTAMPTIMEZONE             |
    | 16/08/2018 11:07:23 | 16/08/2018 11:07:23,079714 | 16/08/2018 11:07:23,079714 +02:00 |
    | 16/08/2018 11:07:23 | 16/08/2018 11:07:23,000000 | 16/08/2018 11:07:23,000000 +02:00 | 
    | 16/08/2018 11:07:23 | 16/08/2018 11:07:23,079714 | 16/08/2018 11:07:23,079714 +02:00 |
    

    如您所见。示例程序确实选择了错误的类型。如果 cmd 中没有明确的类型,ODP.Net 就可以正常工作。

    OPD.Net 具有每个 C#-Type 到 OracleDbTypes 的映射。您不必告诉 Oracle DateTime 是什么!

    https://docs.oracle.com/cd/B28359_01/win.111/b28375/featTypes.htm

    如果你开始在你的 C# 代码中设置类型,你就会得到一个双重声明。您的数据库告诉您的客户如何转换变量。

    如果您将 Db-Column 从 Date 更改为 Timestamp,您也必须更改您的 C#-App!如果您有多个应用程序访问您的数据库,那么您需要做很多工作。

    在某些情况下,例如使用可空类型的数组,您应该设置类型,但通常您不必这样做。

    【讨论】:

    • 除非您指定数据类型,即cmd.Parameters.Add(new OracleParameter("MYDATE", OracleDbType.Date, date2));,否则您的解决方案并不是真正的改进。
    • 这里的实际问题是,日期格式不能作为硬编码值给出。它应该是一个变量。那么只有这将与具有不同时间格式设置的用户兼容
    • @tinkerbell 不,不是。如果你有一个C#-DateTime-variable,你就没有任何 CultureInformation。您决定如何从中创建一个字符串。因此,您还必须告诉 oracle 如何解释创建的字符串。这就是我想说明的。
    • @WernfriedDomscheit 一个很大的改进,因为避免了 sql 注入(在我看来是强制性的)并且 oracle-driver/db 决定如何转换你的 C# 类型到甲骨文类型。如果添加 DbType,您可以修改转换,但您不必这样做!
    • @kara 关键是 C# DateTime 类型是 DateTime 类型,如果您将其指定为 OracleDbType.Date,它将始终是正确的日期。永远不会有任何转换冲突,因为它是“纯”日期时间格式,没有任何 Oracle 将始终以正确方式解释的文化信息。
    【解决方案2】:

    无法识别,因为没有这样的“M”参数,这里是 TO_DATE()https://www.techonthenet.com/oracle/functions/to_date.php987654321@的完整文档

    试试这个,正确的格式掩码应该是to_date('11/15/18', 'MM/DD/YY')

    编辑:

    为避免此问题,您最好在查询中使用参数。这是一个小例子:

     DateTime date = //get your date here
    
     string myQuery= "SELECT * FROM xxx WHERE yyy <= :pDate";
    
     OracleCommand oraCmd = new OracleCommand();
     oraCmd.CommandType = CommandType.Text;
     oraCmd.Connection = OracleConnectionSource; // your connection
    
     OracleParameter oraParam = new OracleParameter();
     oraParam = oraCmd.Parameters.Add("pDate", OracleDbType.Date, date, ParameterDirection.Input);
    
     oraCmd.CommandText = myQuery;
     OracleDataReader oraDataReader = oraCmd.ExecuteReader();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-18
      • 2017-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多