在数据库中更新 Oracle TIMESTAMP(0) WITH TIME ZONE 值的正确方法

Posted

技术标签:

【中文标题】在数据库中更新 Oracle TIMESTAMP(0) WITH TIME ZONE 值的正确方法【英文标题】:Proper method to update Oracle TIMESTAMP(0) WITH TIME ZONE value in database 【发布时间】:2016-11-28 16:29:49 【问题描述】:

我正在尝试更新具有TIMESTAMP(0) WITH TIMEZONE 类型列的表。

我尝试了几种方法都没有成功,因为TIMESTAMP 写入数据库的方式没有偏移格式,例如美国东部时间的-05:00。它使用AMERICA/NEW_YORK 作为时区保存,这会导致无法正确处理此问题的另一个应用程序出现问题。

当前:28-NOV-16 10.51.43.000000000 AM AMERICA/NEW_YORK

希望:28-NOV-16 10.51.43.000000000 AM -05:00

这里的许多帖子主要是在从数据库中检索数据时对其进行格式化;其他示例使用 SqlPlus 进行描述,而不是在 C# 中。

Using(OracleConnection conn = new OracleConnection(......))

        OracleCommand cmd = new OracleCommand();
        cmd.Connection = conn;
        cmd.BindByName = true;
        cmd.CommandText = "update customer set email_addr = :EMAIL, modified_date= SYSDATE 
    where cust_id = :CUSTID";

        conn.Open();
        cmd.Parameters.Add("EMAIL", OracleDbType.Varchar2).Value = txtEmail.Text;
        cmd.Parameters.Add("MODIFIED_DATE", OracleDbType.Varchar2).Value = OracleDate.GetSysDate().ToOracleTimeStamp();
        cmd.Parameters.Add("CUSTID", OracleDbType.Decimal).Value =Convert.ToDecimal(Session["ID"]);

    cmd.ExecuteNonQuery();

我也尝试过modified_date=to_timestamp(:modified_date, 'MM/DD/YYYY HH:mi:ss'),但这会生成异常,因为时区格式不正确。

在 C# 中完成此任务的正确方法是什么?日期/时间戳列是否应该始终使用字符串转换来写入?

【问题讨论】:

【参考方案1】:

您的代码中似乎有错字。您指定了 2 个绑定变量,但您尝试绑定 3 个值。

OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.BindByName = true;
cmd.CommandText = "update customer set email_addr = :EMAIL, modified_date= :MODIFIED_DATE
where cust_id = :CUSTID";

conn.Open();
cmd.Parameters.Add("EMAIL", OracleDbType.Varchar2).Value = txtEmail.Text;
cmd.Parameters.Add("MODIFIED_DATE", OracleDbType.TimeStampTZ).Value = OracleDate.GetSysDate().ToOracleTimeStamp();
cmd.Parameters.Add("CUSTID", OracleDbType.Decimal).Value =Convert.ToDecimal(Session["ID"]);
cmd.ExecuteNonQuery();

OracleDbType 不是Varchar2,必须是TimeStampTZ

但是,您似乎正在使用 DevArt 的 Oracle 数据提供程序。当我检查documentation 时,它们似乎不支持数据类型TIMESTAMP WITH TIME ZONE

我看到了几种解决方法。

在对数据库运行操作之前,将SESSIONTIMEZONE 设置为-05:00,例如:

cmd.CommandText = "ALTER SESSION SET TIME_ZONE = '-05:00'";
cmd.ExecuteNonQuery();

那么你的C#代码可以是这样的:

OracleCommand cmd = new OracleCommand();
cmd.Connection = conn;
cmd.BindByName = true;
cmd.CommandText = "update customer set email_addr = :EMAIL, modified_date= CURRENT_TIMESTAMP
where cust_id = :CUSTID";

conn.Open();
cmd.Parameters.Add("EMAIL", OracleDbType.Varchar2).Value = txtEmail.Text;
cmd.Parameters.Add("CUSTID", OracleDbType.Decimal).Value =Convert.ToDecimal(Session["ID"]);
cmd.ExecuteNonQuery();

CURRENT_TIMESTAMP 以TIMESTAMP WITH TIME ZONE 数据类型返回会话时区中的当前时间。

您也可以在SQL中指定时区,例如:

cmd.CommandText = "update customer set email_addr = :EMAIL, 
       modified_date= SYSTIMESTAMP AT TIME ZONE '-05:00'
   where cust_id = :CUSTID";

当您使用TIMESTAMP 更新TIMESTAMP WITH TIME ZONE 值时,Oracle 将使用时区-05:00 隐式转换为TIMESTAMP WITH TIME ZONE

注意,America/New_York 有夏令时,即您必须使这些命令更加动态,并且每年两次在 -05:00-04:00 之间切换(如果您将使用时区区域,例如America/New_York)。

另一种解决方法是在您的应用程序中运行 ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'DD-MON-YY HH.MI:SS.FF AM TZH:TZM',它无法处理像 AMERICA/NEW_YORK 这样的时区名称。

【讨论】:

这正是我所需要的,它也回答了我关于设置会话参数的另一个问题。谢谢!顺便说一句,当我使用 SYSTIMESTAMP 时,我会自动获得正确的时区,所以我实际上不需要使用 AT TIME ZONE。再次感谢您。

以上是关于在数据库中更新 Oracle TIMESTAMP(0) WITH TIME ZONE 值的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

关于sybase的timestamp

ORACLE 最后表数据更新的时间

Oracle中插入timestamp类型的值

Oracle中timestamp与date时间转换问题

oracle中date类型怎么转换成timestamp类型?

oracle timestamp类型数据怎么转换成数值