【问题标题】:ORA-00936: missing expression - Line 62ORA-00936: 缺少表达式 - 第 62 行
【发布时间】:2019-10-14 02:53:54
【问题描述】:

几个小时以来,我一直试图找出 Execute Immediate 中的 Insert 语句有什么问题,但没有运气。确保我没有遗漏任何逗号或输入任何不正确的字符。

我已经浏览了 SO 和其他网站上的所有答案,试图找出我可能做错了什么,但没有运气。

运行此函数会导致以下错误(错误行以“-->”开头,请忽略它,因为它只是为了突出显示目的):

ORA-00936: missing expression
ORA-06512: at "BDW_AMPS.COUNT_RECORDS", line 62

这里是函数的 PL/SQL 代码:

CREATE OR REPLACE FUNCTION count_records (
    p_test_case_id   IN NUMBER,
    p_table_name     IN VARCHAR2
) RETURN VARCHAR2 IS

    v_amt_recs            INT;
    v_test_result         VARCHAR2(10);
    v_threshold_val       VARCHAR2(10);
    v_test_suite_table    VARCHAR2(100);
    v_test_result_id      NUMBER;
    v_batch_id            NUMBER;
    v_report_id           NUMBER;
    v_test_seq_no         NUMBER;
    v_session_name        VARCHAR2(100);
    v_error_description   VARCHAR2(100);
    v_process_by          VARCHAR2(100);
BEGIN
    v_test_suite_table := 'bdw_amps.spares_bdw_test_suite';
    v_process_by := 'INFORMATICA';
    EXECUTE IMMEDIATE 'SELECT THRESHHOLD_VALUE FROM '
                      || v_test_suite_table
                      || ' WHERE TEST_CASE_ID = '
                      || p_test_case_id
    INTO v_threshold_val;
    EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || p_table_name
    INTO v_amt_recs;

    EXECUTE IMMEDIATE 'SELECT BDW_AMPS.SPARES_TEST_SEQ_ID_SEQ.NEXTVAL FROM DUAL'
    INTO v_test_result_id;

    EXECUTE IMMEDIATE 'SELECT MAX(BATCH_ID) FROM BDW_AMPS.spares_bdw_session_audit 
     WHERE SESSION_NAME=(SELECT SESSION_NAME FROM '
                      || v_test_suite_table
                      || ' WHERE TEST_CASE_ID = '
                      || p_test_case_id
                      || ')'
    INTO v_batch_id;

    EXECUTE IMMEDIATE 'SELECT REPORT_ID FROM '
                      || v_test_suite_table
                      || ' WHERE TEST_CASE_ID = '
                      || p_test_case_id
    INTO v_report_id;

    EXECUTE IMMEDIATE 'SELECT TEST_SEQ FROM '
                      || v_test_suite_table
                      || ' WHERE TEST_CASE_ID = '
                      || p_test_case_id
    INTO v_test_seq_no;

    EXECUTE IMMEDIATE 'SELECT SESSION_NAME FROM '
                      || v_test_suite_table
                      || ' WHERE TEST_CASE_ID = '
                      || p_test_case_id
    INTO v_session_name;

    IF
        v_amt_recs > v_threshold_val
    THEN
        v_test_result := 'PASS';

    --> EXECUTE IMMEDIATE 'INSERT INTO BDW_AMPS.spares_bdw_test_results(
        TEST_RESULT_ID, 
        BATCH_ID, 
        REPORT_ID, 
        TEST_CASE_ID, 
        TEST_SEQ_NO, 
        TABLE_NAME, 
        SESSION_NAME, 
        TEST_RESULT, 
        PROCESS_DATE, 
        PROCESS_BY
        )
        VALUES 
        ('|| v_test_result_id || ',
        ' || v_batch_id || ',
        ' || v_report_id || ',
        ' || p_test_case_id || ',
        ' || v_test_seq_no || ',
        ' || p_table_name || ',
        ' || v_session_name || ',
        ' || v_test_result || ',
        SYSDATE,
        ' || v_process_by || '
        )';

    EXECUTE IMMEDIATE 'commit';

    ELSE
        v_test_result := 'FAIL';
        v_error_description := 'Count: ' || v_amt_recs || ' is greater than threshold value: ' || v_threshold_val;

    EXECUTE IMMEDIATE 'INSERT INTO BDW_AMPS.spares_bdw_test_results(
    TEST_RESULT_ID, 
    BATCH_ID, 
    REPORT_ID, 
    TEST_CASE_ID, 
    TEST_SEQ_NO, 
    TABLE_NAME, 
    SESSION_NAME, 
    TEST_RESULT,
    ERROR_DESCRIPTION,
    PROCESS_DATE, 
    PROCESS_BY
    )
    VALUES (
    '|| v_test_result_id || ',
    ' || v_batch_id || ',
    ' || v_report_id || ',
    ' || p_test_case_id || ',
    ' || v_test_seq_no || ',
    ' || p_table_name  || ',
    ' || v_session_name || ',
    ' || v_test_result || ',
    ' || v_error_description || ',
    SYSDATE,
    ' || v_process_by  || '
    )';
    EXECUTE IMMEDIATE 'commit';

    END IF;

    RETURN v_test_result;
END;

【问题讨论】:

  • 寻求调试帮助的问题不在主题范围内
  • 嗨 Gautham,欢迎您将 EXECUTE IMMEDIATE 'commit'; 替换为 commit;
  • 对不起,从您突出显示的行 (61) 开始删除所有 execute immediate。你不需要
  • values 子句中的字符串值需要被引用,因此您需要为这些值连接一些额外的引号字符。让代码打印出生成的语句,问题应该很清楚。 (尽管 Barbaros 有一个很好的观点,即这里似乎不需要动态 SQL。)
  • 谢谢@WilliamRobertson 和Barbaros 我用你的建议更新了帖子中的代码,现在它按预期工作了。我很好奇为什么它以前不起作用。顺便说一句,感谢您的建议。

标签: oracle plsql


【解决方案1】:

假设这是一个真正需要动态的简化示例,一个问题是字符串值没有被引用。 (如果您有日期值,它们也需要特殊处理。)

例如:

create table demo (numcol number, stringcol varchar2(20));

declare
    l_num number := 123;
    l_string varchar2(20) := 'Kittens';
    l_sql long := 'insert into demo(numcol, stringcol) values ('||l_num||', '||l_string||')';
begin
    dbms_output.put_line(l_sql);
    execute immediate l_sql;
end;
/

生成的代码:

insert into demo(numcol, stringcol) values (123, Kittens)

失败:

ORA-00984: column not allowed here

错误会因字符串的内容而异。例如,如果它包含空格:

declare
    l_num number := 123;
    l_string varchar2(20) := 'Kittens are cute';
    l_sql long := 'insert into demo(numcol, stringcol) values ('||l_num||', '||l_string||')';
begin
    dbms_output.put_line(l_sql);
    execute immediate l_sql;
end;
/

生成的代码:

insert into demo(numcol, stringcol) values (123, Kittens are cute)

ORA-00917: missing comma

或逗号:

declare
    l_num number := 123;
    l_string varchar2(20) := 'Kittens, Puppies';
    l_sql long := 'insert into demo(numcol, stringcol) values ('||l_num||', '||l_string||')';
begin
    dbms_output.put_line(l_sql);
    execute immediate l_sql;
end;
/

insert into demo(numcol, stringcol) values (123, Kittens, Puppies)

ORA-00913: too many values

你需要建立报价:

declare
    l_num number := 123;
    l_string varchar2(20) := 'Kittens, Puppies';
    l_sql long := 'insert into demo(numcol, stringcol) values ('||l_num||', '''||l_string||''')';
begin
    dbms_output.put_line(l_sql);
    execute immediate l_sql;
end;
/

让你生成

insert into demo(numcol, stringcol) values (123, 'Kittens, Puppies')

(如果字符串可以包含引号字符,则需要更多工作。)

始终将动态 SQL 构建为变量并在失败时打印或记录它是值得的,因为当您看到代码时通常很清楚问题所在。

另外一点是,像这样连接值是资源密集型的,因为 Oracle 试图缓存 SQL 语句以供重用,因此它们将被单独解析和优化并占用缓存中的空间,但它们永远不会被重用。如果这将经常使用不同的值运行,您应该考虑通过using clause of execute immediate 使用绑定变量。

【讨论】:

  • 谢谢你的解释。稍后当我开始查看失败点(第 62 行)之后的代码时,我才意识到关于字符串的这一点。
  • 关于打印出你的动态 SQL 语句的评论真的是一个很好的提示!再次感谢:)
【解决方案2】:

您的 DML(INSERT) 语句不需要 EXECUTE IMMEDIATE 语句。所以,在第 61 行之后删除它们:

CREATE OR REPLACE FUNCTION count_records (
    p_test_case_id   IN NUMBER,
    p_table_name     IN VARCHAR2
) RETURN VARCHAR2 IS

    v_amt_recs            INT;
    v_test_result         VARCHAR2(10);
    v_threshold_val       VARCHAR2(10);
    v_test_suite_table    VARCHAR2(100);
    v_test_result_id      NUMBER;
    v_batch_id            NUMBER;
    v_report_id           NUMBER;
    v_test_seq_no         NUMBER;
    v_session_name        VARCHAR2(100);
    v_error_description   VARCHAR2(100);
    v_process_by          VARCHAR2(100);
BEGIN
    v_test_suite_table := 'bdw_amps.spares_bdw_test_suite';
    v_process_by := 'INFORMATICA';

    EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || p_table_name
    INTO v_amt_recs;

    v_test_result_id := BDW_AMPS.SPARES_TEST_SEQ_ID_SEQ.NEXTVAL;

    EXECUTE IMMEDIATE 'SELECT MAX(BATCH_ID) FROM BDW_AMPS.spares_bdw_session_audit 
     WHERE SESSION_NAME=(SELECT SESSION_NAME FROM '
                  || v_test_suite_table
                      || ' WHERE TEST_CASE_ID = '
                      || p_test_case_id
                      || ')'
    INTO v_batch_id;

    EXECUTE IMMEDIATE 'SELECT THRESHHOLD_VALUE, REPORT_ID, TEST_SEQ,SESSION_NAME FROM '
                      || v_test_suite_table
                      || ' WHERE TEST_CASE_ID = :caseId'
    INTO v_threshold_val,v_report_id,v_test_seq_no,v_session_name
    USING p_test_case_id;

    IF
        v_amt_recs > v_threshold_val
    THEN
        v_test_result := 'PASS';

    INSERT INTO BDW_AMPS.spares_bdw_test_results(
        TEST_RESULT_ID, 
        BATCH_ID, 
        REPORT_ID, 
        TEST_CASE_ID, 
        TEST_SEQ_NO, 
        TABLE_NAME, 
        SESSION_NAME, 
        TEST_RESULT, 
        PROCESS_DATE, 
        PROCESS_BY
        )
        VALUES 
        ( v_test_result_id ,
          v_batch_id ,
          v_report_id ,
          p_test_case_id ,
          v_test_seq_no ,
          p_table_name ,
          v_session_name ,
          v_test_result ,
          SYSDATE,
         v_process_by 
        );

    commit;

    ELSE
        v_test_result := 'FAIL';
        v_error_description := 'Count: ' || v_amt_recs || ' is greater than threshold value: ' || v_threshold_val;

    INSERT INTO BDW_AMPS.spares_bdw_test_results(
    TEST_RESULT_ID, 
    BATCH_ID, 
    REPORT_ID, 
    TEST_CASE_ID, 
    TEST_SEQ_NO, 
    TABLE_NAME, 
    SESSION_NAME, 
    TEST_RESULT,
    ERROR_DESCRIPTION,
    PROCESS_DATE, 
    PROCESS_BY
    )
    VALUES (
    v_test_result_id ,
     v_batch_id ,
     v_report_id ,
     p_test_case_id ,
     v_test_seq_no ,
     p_table_name  ,
     v_session_name ,
     v_test_result ,
     v_error_description ,
    SYSDATE,
     v_process_by  
    );
    commit;

    END IF;

    RETURN v_test_result;
END;

由于动态表名,它们的用法适用于SELECT 语句。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-18
    • 2017-08-31
    • 1970-01-01
    相关资源
    最近更新 更多