【问题标题】:TO_DATE issue with Oracle DBMS_SQL.EXECUTE functionOracle DBMS_SQL.EXECUTE 函数的 TO_DATE 问题
【发布时间】:2012-07-28 15:09:02
【问题描述】:

我正在尝试使用 DBMS_SQL.EXECUTE 函数调用 Oracle 存储过程。我将 PL/SQL 块作为输入参数传递给使用 DBMS_SQL.EXECUTE 函数调用 DB 过程的函数。该过程有一个字符串参数,我使用 TO_DATE 函数以字符串格式传递当前日期。有关我用来调用该过程的语法,请参见下文。调用该过程时,传递的日期将作为 DATE 数据类型保存在数据库中。我面临的问题是日期的时间部分被截断,时间变为上午 12:00,而与传递给函数的时间无关。有人可以告诉我我做错了什么吗?

用于调用函数的 SQL 块:

plsql_block =BEGIN P_USER_TIME(to_date('21-JUL-2012 03:30:30','DD-MON-YYYY HH24:MI:SS')); Return; End;

调用过程的函数的SQL语法:

ret_int := DBMS_SQL.OPEN_CURSOR;

DBMS_SQL.PARSE(ret_int,plsql_block,DBMS_SQL.NATIVE);

ret_int_execute := DBMS_SQL.EXECUTE(ln_dbms_cur);

预言机代码块PFB

我正在使用一个函数,该函数将日期作为 varchar 输入,然后 n 将其传递给另一个过程。我可以看到在某些情况下没有插入与日期相关的过去时间。请在下面找到我的功能

iv_plsql4 :='10-08-2012 07:30:30';
ln_dbms_cur := DBMS_SQL.OPEN_CURSOR;
iv_plsql2 := BEGIN PKG_PRADEEP.P_INSERTDATE(to_date(iv_plsql4,'DD-MM-YYYY HH24:MI:SS'));

DBMS_OUTPUT.put_line(iv_plsql);
DBMS_SQL.PARSE(ln_dbms_cur,iv_plsql,DBMS_SQL.NATIVE);
ln_cur_execute := DBMS_SQL.EXECUTE(ln_dbms_cur);
This code inserts the date in to the database but the time comes as 12:00 A.M.

但如果我更改字符串 iv_plsql2 如下所示,日期将插入 TIME 字段。

iv_plsql2 := BEGIN PKG_PRADEEP.P_INSERTDATE(to_date('10-AUG-2012 07:30:30','DD-MM-YYYY HH24:MI:SS'));

谁能解释为什么会这样?

【问题讨论】:

  • 您的 PL/SQL 块似乎除了调用 P_USER_TIME 之外什么也没做。此过程是否会截断日期?没必要return顺便说一句。
  • 我认为我们真的需要看看函数和过程是如何声明的,以及它们是如何被调用的;你是如何构建plsql_block 字符串的?我怀疑to_date() 发生的时间比您想象的要早,并且date 正在使用您的NLS_DATE_FORMAT(例如DD-MON-YYYY)隐式转换回varchar2 - 因为该过程有一个字符串参数,而不是日期- 正是它失去了时间部分。
  • Ben- 我不确定该过程是否会截断日期。

标签: java sql oracle function stored-procedures


【解决方案1】:

我对此的第一个立场是 STP P_USER_TIME() 正在截断时间戳。但是,如果您确定它可能没有这样做,您可以尝试-

DECLARE
   ret_int INTEGER;
   plsql_block  VARCHAR2(1000);
BEGIN
   plsql_block :='BEGIN P_USER_TIME(to_timestamp(''21-JUL-2012 03:30:30'',''DD-MON-YYYY HH24:MI:SS'')); END;';
   ret_int := DBMS_SQL.OPEN_CURSOR;
   DBMS_SQL.PARSE(ret_int,plsql_block,DBMS_SQL.NATIVE);
   ret_int_execute := DBMS_SQL.EXECUTE(ret_int);
   DBMS_SQL.CLOSE_CURSOR(ret_int);
EXCEPTION
WHEN OTHERS THEN
   DBMS_SQL.CLOSE_CURSOR(ret_int);
END;

注意 - 有许多与 ODBC 驱动程序相关的日期时间相关错误。例如 - 错误 11864041 - 时间戳变量从 ODBC 传递为 VARCHAR 导致值损坏 (Oracle Support)

【讨论】:

  • 嗨 Annjawn,感谢您的建议。目前,当我从 SQL 开发人员调用该过程时,该函数工作正常,但是当我再次尝试从我的 java 应用程序调用该函数时,日期被存储时间为 12:00 AM .您是否知道任何此类问题?
  • 是的,JDBC 驱动程序在日期时间戳值方面存在许多问题,尤其是因为日期在 Java 中的处理方式完全不同。您可能会在某个时候克服这个问题,但不能保证它会继续工作。这里最好/安全的选择是将日期列作为 VARCHAR(14) 值 YYYYMMDDHHMISS,然后在根据需要从列中选择时执行 TO_DATE。我们长期面临与 Java 和 Oracle 相关的此类日期相关问题,并且 Oracle 支持记录了许多错误但没有真正的解决方案。
  • @Annjawn - 在varchar2 字段中存储日期是一个糟糕的解决方案。它使优化器感到困惑,使所有交互变得比它需要的更复杂,也有一天会中断,并阻止您在列上使用索引。见this
  • 如果你真的有一个驱动问题无法正确修复,稍微好一点的解决办法是让你传递的任何日期字符串;因此,如果您有 some_date > ? 并且在从 Java 日期生成参数时遇到问题,可以改为使用 some_date > to_date(?,?),将第一个参数设为 SimpleDateFormatter 中的字符串,并将第二个参数设为相同的格式掩码(或 DB 等效项)。在这种情况下,OP 无论如何都会将字符串传递给函数,因此它可能只是将 Java 日期转换为具有意外格式的字符串或其他东西。
  • @AlexPoole 我知道这是一个可怕的解决方案。但是您提供的解决方案可能也不起作用,因为问题仍然存在于 oc4j/驱动程序级别,Java 中的任何日期格式化程序或任何日期格式在 Oracle 日期数据类型方面都无法正常工作。自过去三年以来,我们的应用程序一直存在此问题,并且没有一个(Oracle/java)能够为我们完全解决此问题。所以我的观点是拥有正确的数据比拥有不正确的数据要好,即使它牺牲了技术诀窍(如索引等)。
【解决方案2】:

您所说的似乎有些矛盾,但流程不是很清楚。您有一个从函数(未命名)调用的过程 (P_USER_TIME?)。你的过程(至少看看它是如何声明的会很有帮助)接受一个字符串参数,但你传递给它的是一个date,而不是varchar2 值。由于我们没有您的实际程序,我们来编一个:

create or replace procedure p_user_time(p_time varchar2) is
    l_time date;
begin
    dbms_output.put_line('Parameter p_time: ' || p_time);
    l_time := to_date(p_time, 'DD-MON-YYYY HH24:MI:SS');
    dbms_output.put_line('Converted l_time: ' ||
        to_char(l_time, 'DD-MON-YYYY HH24:MI:SS'));
end;
/

如果我用你传递的字符串plsql_block 来调用它,我会得到:

alter session set nls_date_format = 'DD-MON-YYYY';
set serveroutput on

exec P_USER_TIME(to_date('21-JUL-2012 03:30:30','DD-MON-YYYY HH24:MI:SS'));

Session altered.

Parameter p_time: 21-JUL-2012
Converted l_time: 21-JUL-2012 00:00:00

PL/SQL procedure successfully completed.

所以时间部分丢失了。您正在将表示日期的字符串转换为 date 对象,当它传递给过程时,它会使用您的默认 NLS_DATE_FORMAT 掩码隐式转换回字符串,我猜这可能是 DD-MON-YYYY;所以这相当于做:

exec P_USER_TIME(to_char(to_date('21-JUL-2012 03:30:30','DD-MON-YYYY HH24:MI:SS')));

执行to_char(to_date(...)) 看起来是多余的,但是因为您有一个明确的数据掩码,而另一个隐含的数据掩码,它可能没有达到您的预期。

假设P_USER_TIME 过程期望您传递的特定格式的日期/时间字符串,您应该只传递字符串,而不是尝试自己进行转换:

exec P_USER_TIME('21-JUL-2012 03:30:30');

Parameter p_time: 21-JUL-2012 03:30:30
Converted l_time: 21-JUL-2012 03:30:30

PL/SQL procedure successfully completed.

您还有一个动态调用该过程的函数。再次,让我们组成一个:

create or replace function f_foo return number is
    ln_dbms_cur number;
    ret_int number;
    plsql_block varchar2(256);
begin
     plsql_block := 'BEGIN P_USER_TIME(to_date(''21-JUL-2012 03:30:30'',''DD-MON-YYYY HH24:MI:SS'')); END;';

     ln_dbms_cur := DBMS_SQL.OPEN_CURSOR;
     DBMS_SQL.PARSE(ln_dbms_cur, plsql_block, DBMS_SQL.NATIVE);
     ret_int := DBMS_SQL.EXECUTE(ln_dbms_cur);
     DBMS_SQL.CLOSE_CURSOR(ln_dbms_cur);
     return ret_int;
end;
/

var rc number;

exec :rc := f_foo;

Parameter p_time: 21-JUL-2012
Converted l_time: 21-JUL-2012 00:00:00

PL/SQL procedure successfully completed.

所以同样的事情发生了。如果plsql_block的构造简化为:

    plsql_block := 'BEGIN P_USER_TIME(''21-JUL-2012 03:30:30''); END;';

然后你得到:

Parameter p_time: 21-JUL-2012 03:30:30
Converted l_time: 21-JUL-2012 03:30:30

PL/SQL procedure successfully completed.

再次阅读问题,它实际上可能是一个更简单的潜在问题。 你说'......我使用 to_date 函数以字符串格式将当前日期传递给它的字符串参数'。 如果按字面意思解释,则表明您可能只是在使用to_date 而应该使用to_char; 如果您真的想要使函数中的行类似于以下内容的当前时间:

 plsql_block := 'BEGIN P_USER_TIME(to_char(sysdate, ''DD-MON-YYYY HH24:MI:SS'')); END;';

或者直接调用过程:

exec P_USER_TIME(to_char(sysdate, 'DD-MON-YYYY HH24:MI:SS'));

Parameter p_time: 31-JUL-2012 09:38:43
Converted l_time: 31-JUL-2012 09:38:43

PL/SQL procedure successfully completed.

已编辑以查看作为评论发布的 Java 代码

您的函数现在似乎有两个参数,其中一个是您要执行的块;并返回一个游标。我将(再次)猜测光标正在返回已插入的内容,因此我更改了我的虚拟程序以将日期/时间插入表中,并使用我的函数来检索它。当然,如果您发布一套完整的代码来演示您遇到的问题,这会容易得多。

create or replace procedure p_user_time(p_time varchar2) is
    l_time date;
begin
    dbms_output.put_line('Parameter p_time: ' || p_time);
    l_time := to_date(p_time, 'DD-MON-YYYY HH24:MI:SS');
    dbms_output.put_line('Converted l_time: ' ||
        to_char(l_time, 'DD-MON-YYYY HH24:MI:SS'));
    insert into cooldude values(l_time);
end;
/

create or replace function f_foo(pNumber number, p_plsql_block in varchar2)
return sys_refcursor is
    ln_dbms_cur number;
    ret_int number;
    plsql_block varchar2(256);
    rc sys_refcursor;
begin    
     ln_dbms_cur := DBMS_SQL.OPEN_CURSOR;
     DBMS_SQL.PARSE(ln_dbms_cur, p_plsql_block, DBMS_SQL.NATIVE);
     ret_int := DBMS_SQL.EXECUTE(ln_dbms_cur);
     DBMS_SQL.CLOSE_CURSOR(ln_dbms_cur);

     open rc for select * from cooldude;
     return rc;
end;
/

我仍然可以毫无问题地从 SQL*Plus 调用它。我可以让 Java 程序执行它:

import java.sql.*;
import java.text.*;
import oracle.jdbc.*;
import oracle.jdbc.pool.OracleDataSource;

public class Cooldude
{

    public static void main(String args[]) throws SQLException 
    {
        String plSqlBlk = "BEGIN P_USER_TIME(to_char(sysdate, 'DD-MON-YYYY HH24:MI:SS')); END;";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        Connection conn;
        OracleDataSource ds = new OracleDataSource();
        ds.setURL("jdbc:oracle:thin:scott/oracle@127.0.0.1:1521:orcl");
        conn = ds.getConnection();

        CallableStatement cstmt = null;
        ResultSet rs = null;
        String output = "";
        System.out.println("******calling SP *******");
        cstmt = conn.prepareCall("{? = call f_foo(?,?)}");
        cstmt.setFetchSize(10000);
        cstmt.registerOutParameter(1, OracleTypes.CURSOR);
        cstmt.setInt(2, 204149885);
        cstmt.setString(3, plSqlBlk);
        cstmt.executeQuery();

        rs = (ResultSet) cstmt.getObject(1);
        while (rs.next())
        {
            Timestamp ts = rs.getTimestamp(1);
            System.out.println(sdf.format(ts));
        }

        if ( conn != null )
        {
            try { conn.close(); } catch ( Exception ex ) {}
            conn = null;
        }
    }
}

javac Cooldude.java && java Cooldude

******calling SP *******
2012-08-11 09:45:07
2012-08-11 09:46:04
2012-08-11 09:54:33

这似乎很好;这具有对 Java 程序的三个调用的输出。

您还没有完全说明为什么您认为 Java 代码中的时间会被截断。我会走得更远......你是基于你的Java显示它显示为00:00:00吗?如果是这样,您是否使用rs.getDate() 而不是rs.getTimestampjava.sql.Date 没有时间部分,与 java.util.Date 不同。 (例如参见this question)。

【讨论】:

  • 嗨,Alex,感谢您的建议。在我的情况下,当我从 SQL 开发人员调用该过程时,该函数目前工作正常,但是当我再次尝试从我的 Java 应用程序调用该函数时,日期被存储时间为 12:00 AM .您是否知道任何此类问题?
  • @Coldude - 请将您的 Java 代码添加到问题中,否则您对实际调用和参数进行任何调试。您可能在调用的某处进行了隐式转换,特别是如果您没有将日期作为适当的参数传递。
  • @Annjawn - '该过程有一个字符串参数,我使用 TO_DATE 函数以字符串格式传递当前日期'? to_dateto_char 之间可能存在混淆。我说过我们需要查看功能和程序规范。
  • CallableStatement cstmt = null;结果集 rs = null; SessionFactory sessionFactory = new Configuration().configure() .buildSessionFactory();会话 = sessionFactory.openSession();事务事务 = session.beginTransaction();字符串输出 = ""; System.out .println("******调用SP *******"); cstmt = session.connection().prepareCall("{? = call f_foo(?,?)}");cstmt.setFetchSize(10000); cstmt.registerOutParameter(1, oracle.jdbc.driver.OracleTypes.CURSOR);cstmt.setInt(2, 204149885); cstmt.setString(3, plSqlBlk); cstmt.executeQuery();
  • PLSQK BLOCK 传递就像 'BEGIN P_USER_TIME(to_char(sysdate, ''DD-MON-YYYY HH24:MI:SS'));结尾;';如果您需要更多详细信息,请告诉我
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-31
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
  • 2014-02-01
相关资源
最近更新 更多