【问题标题】:cx_Oracle 'ORA-01843: not a valid month' with unicode parametercx_Oracle 'ORA-01843: not a valid month' with unicode parameter
【发布时间】:2013-03-02 00:47:17
【问题描述】:

我有以下内容:(使用 ipython)

In [30]: con = cx_Oracle.connect('refill_test02/******@MYDB')

In [31]: cur = con.cursor()

In [32]: cur.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS' NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")

In [33]: cur.execute("select to_date(:0), to_timestamp(:1) from dual", ['2013-03-12', '2013-03-12 08:22:31.332144'])
Out[33]: <__builtin__.OracleCursor on <cx_Oracle.Connection to refill_test02@MYDB>>

In [34]: cur.fetchall()
Out[34]: 
[(datetime.datetime(2013, 3, 12, 0, 0),
  datetime.datetime(2013, 3, 12, 8, 22, 31, 332144))]

In [35]: cur.execute("select to_date(:0), to_timestamp(:1) from dual", [u'2013-03-12', '2013-03-12 08:22:31.332144'])
Out[35]: <__builtin__.OracleCursor on <cx_Oracle.Connection to refill_test02@MYDB>>

In [36]: cur.fetchall()
Out[36]: 
[(datetime.datetime(2013, 3, 12, 0, 0),
  datetime.datetime(2013, 3, 12, 8, 22, 31, 332144))]

In [37]: cur.execute("select to_date(:0), to_timestamp(:1) from dual", [u'2013-03-12', u'2013-03-12 08:22:31.332144'])
---------------------------------------------------------------------------
DatabaseError                             Traceback (most recent call last)
/home/xxxxx/<ipython-input-37-8af80e5fc40c> in <module>()
----> 1 cur.execute("select to_date(:0), to_timestamp(:1) from dual", [u'2013-03-12', u'2013-03-12 08:22:31.332144'])

DatabaseError: ORA-01843: not a valid month


In [38]: cur.execute("select to_date(:0), to_timestamp(:1) from dual", [u'2013-03-12', '2013-03-12 08:22:31.332144'])
---------------------------------------------------------------------------
DatabaseError                             Traceback (most recent call last)
/home/xxxx/<ipython-input-38-bc628f006aa3> in <module>()
----> 1 cur.execute("select to_date(:0), to_timestamp(:1) from dual", [u'2013-03-12', '2013-03-12 08:22:31.332144'])

DatabaseError: ORA-01843: not a valid month


In [39]: cur.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS' NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")

In [40]: cur.execute("select to_date(:0), to_timestamp(:1) from dual", [u'2013-03-12', '2013-03-12 08:22:31.332144'])
Out[40]: <__builtin__.OracleCursor on <cx_Oracle.Connection to refill_test02@MYDB>>

In [41]: cur.fetchall()
Out[41]: 
[(datetime.datetime(2013, 3, 12, 0, 0),
  datetime.datetime(2013, 3, 12, 8, 22, 31, 332144))]

由于某种原因,我不能将 unicode 字符串用于时间戳参数 (IN[37]),更奇怪的是,在我这样做之后,我需要重置会话 NLS 格式,然后才能再次使用普通字符串。

我正在使用: CX_Oracle 5.1.2 蟒蛇2.7.3 甲骨文 10.2.0.1.0

有什么想法吗?

感谢您抽出宝贵时间阅读本文。

【问题讨论】:

    标签: python oracle unicode cx-oracle


    【解决方案1】:

    这实际上是 Oracle 10.5.0.2 和 11.2.0.1 中的一个错误。

    Bug 可以重现如下:

    在会话中设置 NLS_TIMESTAMP_FORMAT。

    使用 unicode 数据运行任何隐式或显式 TO_DATE 转换。

    下一个带有 unicode 数据的隐式或显式 TO_TIMESTAMP 将触发时间戳格式的内部重置。

    所有连续的 TO_TIMESTAMP 都会失败,时间戳的 TO_CHAR 会产生无效的输出。

    这是测试行为的代码:

    ALTER SESSION SET NLS_TERRITORY = 'AMERICA';
    ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS' NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF';
    
    REM --- WORKS:
    SELECT TO_TIMESTAMP('2013-06-24 18:15:10.312') FROM DUAL;
    
    REM --- WORKS:
    SELECT TO_DATE('2013-06-24 18:15:10') FROM DUAL;
    
    REM --- WORKS:
    SELECT TO_TIMESTAMP('2013-06-24 18:15:10.312') FROM DUAL;
    
    REM --- WORKS:
    SELECT TO_TIMESTAMP(x) FROM (SELECT CAST('2013-06-24 18:15:10.312' AS NVARCHAR2(30)) AS X FROM DUAL);
    
    REM --- WORKS:
    SELECT TO_DATE(x) FROM (SELECT CAST('2013-06-24 18:15:10' AS NVARCHAR2(30)) AS X FROM DUAL);
    
    REM --- WORKS:
    SELECT TO_TIMESTAMP('2013-06-24 18:15:10.312') FROM DUAL;
    
    REM !!! FAILS!
    SELECT TO_TIMESTAMP(x) FROM (SELECT CAST('2013-06-24 18:15:10.312' AS NVARCHAR2(30)) AS X FROM DUAL);
    
    REM !!! FAILS!
    SELECT TO_TIMESTAMP('2013-06-24 18:15:10.312') FROM DUAL;
    
    REM --- WORKS:
    SELECT TO_DATE('2013-06-24 18:15:10') FROM DUAL;
    

    【讨论】:

      【解决方案2】:

      基于jtiai问题description,我做了以下解决方法——在调用任何有问题的sql-s(例如oracle 10.5.0.2和11.2.0.1,cx_oracle 5.1.2)之前,再次重置NLS_DATE_FORMAT/NLS_TIMESTAMP_FORMAT:

      cursor.execute(
           "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'"
           " NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
      
      # do execute ...
      cursor.execute(query, params)
      

      【讨论】:

        【解决方案3】:

        我无法使用 RHEL 5、Cx_Oracle 5.1 python 2.4.3 Oracle 11.2.0.3.0 重新创建错误 您是否尝试在 to_date 和 to_timestamp 函数中使用格式字符串?

        https://gist.github.com/fclrc/5435561

        #! /bin/python
        import cx_Oracle
        import platform
        
        print ("Python version: " + platform.python_version())
        print ("cx_Oracle version: " + cx_Oracle.version)
        print ("Oracle client: " + str(cx_Oracle.clientversion()).replace(', ','.'))
        
        connection = cx_Oracle.connect('user/pass@tns')
        cursor = connection.cursor()
        #
        # Option with format strings
        #
        cursor.execute("""select to_date(:arg1,'yyyy-mm-dd'), to_timestamp(:arg2,'yyyy-mm-dd hh24.mi.ss.ff') from dual""", arg1=u'2013-03-12', arg2=u'2013-03-12 08:22:31.332144')
        #
        # Option without format strings
        #
        #cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS' NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
        #cursor.execute("select to_date(:0), to_timestamp(:1) from dual", [u'2013-03-12', u'2013-03-12 08:22:31.332144'])
        
        mydate = cursor.fetchall()
        print mydate
        cursor.close()
        connection.close()
        

        【讨论】:

        • 使用格式字符串可以正常工作,问题是SQL代码是由Django在保存模型时生成的,我想避免手动插入。感谢您的回答。
        猜你喜欢
        • 1970-01-01
        • 2017-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多