【问题标题】:PL/SQL - ORACLE: ORA-01843: not a valid monthPL/SQL - ORACLE: ORA-01843: 不是一个有效的月份
【发布时间】:2016-11-07 08:16:33
【问题描述】:

我想在 oracle 数据库版本 11.2.0.4 上的 PL/SQL ETL 进程内将数据插入带有时间戳列的表中

如果我在 Toad 中运行“从所选行创建 INSERT”,我会得到以下 SQL 命令:

 Insert into xxxx$1
   (ID, ITEM, ITEMSIZE, QUALITY, MATERIAL, 
    COLOUR, IMAGEURL, CREATIONDATE, SAMPLEITEMNUMBER, ITEMNUMBER)
 Values
   (111, 339079775, '1', 'Microfaser PRIMABELLE®', 'TEXTILE', 
    '1 (=creme)', 'url', TO_TIMESTAMP('27.06.2016 15:49:35.000000','DD.MM.YYYY HH24:MI:SS.FF'), 'xxx', 'xxxx');
COMMIT;

在 PL/SQL 中,我生成了一个 VARCHAR2/String,看起来与此类似:

v_sql :=
                                'INSERT INTO xxxx'
                             || p_importpostfix
                             || '  VALUES ('
                             || seq_xxxx.NEXTVAL
                             || ','
                             || v_rec.item_id
                             || ', '''
                             || v_size
                             || ''','''
                             || v_quality
                             || ''','''
                             || v_material
                             || ''','''
                             || v_colour
                             || ''','''
                             || NULL
                             || ''',to_timestamp('''|| to_char( sysdate, 'DD.MM.YYYY HH24:MI:SS')||''',''DD.MM.YYYY HH24:MI:SS.FF''),'''
                             || v_rec.vid
                             || ''','''
                             || v_rec.vid
                             || ''')';

如果我在整个过程之外单独运行该过程,它可以正常工作。 没有错误发生。

如果我运行由数据库 JOB 启动的完整进程,我将得到一个 ORACLE:

ORA-01843: 不是一个有效的月份错误。

我已阅读有关此问题的其他线程,但不了解我的问题。

【问题讨论】:

  • v_sql 的输出在您的程序中为您提供了什么?如果您还没有登录您的程序,请使用 dbms_output.put_line 输出它。
  • 好吧,我已经登录了我的应用程序。我将异常记录在一个单独的表中。我有缺陷的那一行。但是 SQL 看起来不错。我已将其记录在 LOG 表中,在使用 dbms_output.put_line 打印输出之前
  • 这些作业是在同一个数据库中运行还是我们有不同的数据库用于示例。
  • 不妨试试 trunc(systimestamp)
  • 如果您有日志记录,请将 v_sql 的内容写到您的表中,以便您可以查看生成的语句。这应该对帮助您调试问题大有帮助。

标签: database oracle plsql oracle11g toad


【解决方案1】:

不确定是否是问题所在,但按字面意思使用 SYSDATE 似乎要容易得多。

您似乎插入了字符串“NULL”。如果要插入 NULL 值,则不需要使用多个单引号,因为最终字符串不需要单引号来插入 NULL 值。

v_sql :=
                                'INSERT INTO xxxx'
                             || p_importpostfix
                             || '  VALUES ('
                             || seq_xxxx.NEXTVAL
                             || ','
                             || v_rec.item_id
                             || ', '''
                             || v_size
                             || ''','''
                             || v_quality
                             || ''','''
                             || v_material
                             || ''','''
                             || v_colour
                             || ''','
                             || 'NULL,'
                             || 'SYSDATE,'
                             || ''''
                             || v_rec.vid
                             || ''','''
                             || v_rec.vid
                             || ''')';

编辑

您应该使用参数化查询,而不是尝试构建这样的查询。

    v_sql := 'INSERT INTO xxxx' || p_importpostfix || ' VALUES(:1,:2,:3,:4,:5,:7,:8,:9,:10)';
    v_null := NULL;
    EXECUTE IMMEDIATE v_sql USING IN seq_xxxx.NEXTVAL, 
                                  IN v_rec.item_id, 
                                  IN v_size,
                                  IN v_quality,
                                  IN v_material,
                                  IN v_colour,
                                  IN v_null,
                                  IN SYSTIMESTAMP,
                                  IN v_rec.vid,
                                  IN v_rec.vid;

想象一下,如果您的某个值包含 '...

更多信息on EXECUTE IMMEDIATE on Oracle documentation。不过我认为您需要一个 Oracle 帐户。

【讨论】:

  • 感谢您的回复,但我认为您的意思是 SYSTIMESTAMP 而不是 SYSDATE,因为它不是日期,而是时间戳。我已经尝试过了。问题是,我已经动态构建了 sql 语句,然后运行语句并立即执行...
  • 为什么必须动态建立语句?你不会有一组固定的列要插入吗?
  • 感谢您的回复 - || p_importpostfix 是表的暂存后缀,因此列是固定的,但表不是,如果在 Toad 中运行该过程,它可以正常工作。在我看来,立即执行是运行查询的方式。
  • @soa:这并不妨碍您使用 Luc 的答案。可以动态设置表名,但这并不意味着您也必须连接这些值(这很容易出错)。在execute immediate 调用上绑定参数值是正确的方法。
  • @Luc:查看文档不需要 Oracle 帐户。
【解决方案2】:

你能显示目标表的 DESC 吗? 确保数据类型不匹配?

每当您构建这些动态字符串时,我总是发现使用“q”引号格式来帮助减少 所有这些重复/转义的引号......使它更容易阅读:

从 TOAD 的查询开始:

  Insert into xxxx$1                                                                                           
  (ID, ITEM, ITEMSIZE, QUALITY, MATERIAL,                                                                      
     COLOUR, IMAGEURL, CREATIONDATE, SAMPLEITEMNUMBER, ITEMNUMBER)                                             
  Values                                                                                                       
  (111, 339079775, '1', 'Microfaser PRIMABELLE®', 'TEXTILE',                                                   
     '1 (=creme)', 'url', TO_TIMESTAMP('27.06.2016 15:49:35.000000','DD.MM.YYYY HH24:MI:SS.FF'), 'xxx', 'xxxx');

去掉分号,将 q'[ ]' 环绕每一行:

  v_sql :=
  q'[ Insert into xxxx$1                                                                                            ]'||CHR(10)||
  q'[ (ID, ITEM, ITEMSIZE, QUALITY, MATERIAL,                                                                       ]'||CHR(10)||
  q'[    COLOUR, IMAGEURL, CREATIONDATE, SAMPLEITEMNUMBER, ITEMNUMBER)                                              ]'||CHR(10)||
  q'[ Values                                                                                                        ]'||CHR(10)||
  q'[ (111, 339079775, '1', 'Microfaser PRIMABELLE®', 'TEXTILE',                                                    ]'||CHR(10)||
  q'[    '1 (=creme)', 'url', TO_TIMESTAMP('27.06.2016 15:49:35.000000','DD.MM.YYYY HH24:MI:SS.FF'), 'xxx', 'xxxx') ]'||CHR(10)||

添加 CHR(10) 只是为了使字符串更具可读性 - 这是为了我们的利益。不是甲骨文的。 接下来,添加一个 RTRIM,因为所有这些额外的空格都可以使字符串变大,非常快:) (对于较大的字符串,我使用了相同技术的 CLOB)

  v_sql :=
  RTRIM(q'[ Insert into xxxx$1                                                                                            ]')||CHR(10)||
  RTRIM(q'[ (ID, ITEM, ITEMSIZE, QUALITY, MATERIAL,                                                                       ]')||CHR(10)||
  RTRIM(q'[    COLOUR, IMAGEURL, CREATIONDATE, SAMPLEITEMNUMBER, ITEMNUMBER)                                              ]')||CHR(10)||
  RTRIM(q'[ Values                                                                                                        ]')||CHR(10)||
  RTRIM(q'[ (111, 339079775, '1', 'Microfaser PRIMABELLE®', 'TEXTILE',                                                    ]')||CHR(10)||
  RTRIM(q'[    '1 (=creme)', 'url', TO_TIMESTAMP('27.06.2016 15:49:35.000000','DD.MM.YYYY HH24:MI:SS.FF'), 'xxx', 'xxxx') ]')||CHR(10)||

接下来,我们需要为您要替换的每个值放置“占位符”:

  v_sql :=
  RTRIM(q'[ Insert into xxxx<tab_name>                                                                                    ]')||CHR(10)||
  RTRIM(q'[ (ID, ITEM, ITEMSIZE, QUALITY, MATERIAL,                                                                       ]')||CHR(10)||
  RTRIM(q'[    COLOUR, IMAGEURL, CREATIONDATE, SAMPLEITEMNUMBER, ITEMNUMBER)                                              ]')||CHR(10)||
  RTRIM(q'[ Values                                                                                                        ]')||CHR(10)||
  RTRIM(q'[ (seq_xxxx.nextval, 339079775, '1', 'Microfaser PRIMABELLE®', 'TEXTILE',                                       ]')||CHR(10)||
  RTRIM(q'[    '1 (=creme)', 'url', TO_TIMESTAMP('27.06.2016 15:49:35.000000','DD.MM.YYYY HH24:MI:SS.FF'), 'xxx', 'xxxx') ]')||CHR(10)||

剩下的,使用绑定变量:

  v_sql :=
  RTRIM(q'[ Insert into xxxx<tab_name>                                         ]')||CHR(10)||
  RTRIM(q'[ (ID, ITEM, ITEMSIZE, QUALITY, MATERIAL,                            ]')||CHR(10)||
  RTRIM(q'[    COLOUR, IMAGEURL, CREATIONDATE, SAMPLEITEMNUMBER, ITEMNUMBER)   ]')||CHR(10)||
  RTRIM(q'[ Values                                                             ]')||CHR(10)||
  RTRIM(q'[ (seq_xxxx.nextval, :1, :2, :3, :4,                                 ]')||CHR(10)||
  RTRIM(q'[    :5, NULL, SYSTIMESTAMP, :6, :7 )                                ]')||CHR(10)||

接下来,在运行时,您替换您的表名“piece”(这里需要动态 sql 的唯一原因......)

  v_runsql := REPLACE ( v_sql, '<tab_name>', p_importpostfix );

现在您可以做几件事...通过显示 SQL 来验证它:

  dbms_output.put_line ( v_runsql );

然后执行它,使用“正确”的绑定变量:

  execute immediate v_runsql USING v_rec.item_id, v_size, v_quality, v_material, v_colour, v_rec.vid, v_rec.vid;

【讨论】:

    猜你喜欢
    • 2022-01-22
    • 2012-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-04
    • 2012-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多