使用Oracle表类型执行动态本机SQL时出现无效标识符错误

使用Oracle表类型执行动态本机SQL时出现无效标识符错误,sql,oracle,dynamic,types,procedure,Sql,Oracle,Dynamic,Types,Procedure,我们已经创建了Oracle表类型。我们已经创建了一个数组。这样做是因为我们不知道可以得到多少值,这对于sql查询中的IN子句来说可能太多了 --代码片段----- 以上所有测试都是编译的,当您使用很少的testCode值运行TEST_PROC过程时,它将失败,并出现testCode标识符无效的错误 在该过程中,在我的控制台中打印的最终sql语句是正确的,当您在过程中以静态sql语句的形式运行它时,它不会出现任何错误。但在动态sql中,它失败了。 我尝试使用DYNAMIC_SQL包执行,但结果是相

我们已经创建了Oracle表类型。我们已经创建了一个数组。这样做是因为我们不知道可以得到多少值,这对于sql查询中的IN子句来说可能太多了

--代码片段-----

以上所有测试都是编译的,当您使用很少的testCode值运行TEST_PROC过程时,它将失败,并出现testCode标识符无效的错误

在该过程中,在我的控制台中打印的最终sql语句是正确的,当您在过程中以静态sql语句的形式运行它时,它不会出现任何错误。但在动态sql中,它失败了。 我尝试使用DYNAMIC_SQL包执行,但结果是相同的错误。 此外,我还尝试将其作为“table(testCodes)”的绑定变量。这也失败了

请给出建议。

您正在使用,因此您必须告诉Oracle哪个单词是标识符,哪个单词是变量

考虑以下直接在SQLPlus中运行的语句:

select t.a,t.b, t.c from test t, table(testCodes) tc
它将失败,因为数据库中没有名为
testCodes
的对象。您必须告诉SQL引擎,
testCodes
实际上是一个变量。您必须这样做,因为您选择使用动态SQL,而静态SQL中的变量绑定是自动的

在大多数情况下,可以用与标准变量相同的方式绑定“对象”变量。在PL/SQL中,有几种方法可以做到这一点,例如,对于游标,您可以使用

SQL> DECLARE
  2     l_cur SYS_REFCURSOR;
  3     l_tab inputcodeArr := inputcodeArr(INPUTCODE('A'), INPUTCODE('B'));
  4     l_obj varchar2(100);
  5  BEGIN
  6     OPEN l_cur FOR 'SELECT pc FROM TABLE(:my_variable)' -- notice the ":"
  7        USING l_tab; -- binding by position
  8     LOOP
  9        FETCH l_cur
 10           INTO l_obj;
 11        EXIT WHEN l_cur%NOTFOUND;
 12        dbms_output.put_line(l_obj);
 13     END LOOP;
 14     CLOSE l_cur;
 15  END;
 16  /

A
B

PL/SQL procedure successfully completed
但是,在您的情况下,我不需要使用动态SQL,因为您可以有条件地打开游标:

CREATE OR REPLACE PROCEDURE "TEST_PROC"(testCodes   IN inputcodeArr,
                                        timeHorizon IN NUMBER,
                                        p_recordset OUT SYS_REFCURSOR) AS
BEGIN
   IF testCodes IS NOT NULL THEN
      OPEN p_recordset FOR
         SELECT t.a, t.b, t.c FROM test t, TABLE(testCodes) tc 
          WHERE tc.pc = t.NAME;
   ELSE
      OPEN p_recordset FOR
         SELECT t.a, t.b, t.c FROM test t;
   END IF;
END TEST_PROC;
我的建议是尽可能长时间地使用静态SQL,因为使用动态SQL更容易出错


更新以下评论:

如果您的输入数量不是恒定的,并且您必须使用动态SQL,因为有许多过滤器组合,那么您可以使用以下策略:

CREATE OR REPLACE PROCEDURE "TEST_PROC"(testCodes   IN inputcodeArr,
                                        timeHorizon IN NUMBER,
                                        p_recordset OUT SYS_REFCURSOR) AS
   l_sql LONG := 'SELECT t.a, t.b, t.c FROM test t WHERE';
BEGIN
   -- filter #1
   IF testCodes IS NOT NULL THEN
      l_sql := l_sql || ' t.name IN (SELECT pc FROM TABLE(:filter1))';
   ELSE
      l_sql := l_sql || ' :filter1 IS NULL';
   END IF;
   -- filter #2
   IF timeHorizon IS NOT NULL THEN
      l_sql := l_sql || ' AND t.horizon = :filter2';
   ELSE
      l_sql := l_sql || ' AND :filter2 IS NULL';
   END IF;
   -- open cursor
   OPEN p_recordset FOR l_sql USING testCodes, timeHorizon;
END TEST_PROC;
/

我要确保最终的SQL始终具有相同顺序的相同数量的变量,但是过滤器为NULL的每个条件都是同义反复(
NULL为NULL
)。

我同意你的观点。但是我不能像你建议的那样使用第二种方法,因为我有大约7个输入可以这样来。所以组合的数量太多了。正如您所提到的,我尝试过这样修改代码。如果testCodes不为null,则var_sqlStmt:=var_sqlStmt | |',表(:vtest)tc';var_sqlStmt:=var_sqlStmt | |'其中tc.pc=t.name';如果结束;使用“testcode”打开var_sqlStmt的p_记录集;这没有替换变量,出现错误。同意,在您的情况下,您必须使用动态SQL。我将更新我的答案,以说明在输入变量的数量不是恒定的情况下,如何仍然使用使用的一般原则。同样,我们使用IN子句,希望避免使用该子句(因为限制为1000项),因此我们使用oracle类型的表。另外,我正在使用表(:filter1),但它并没有在运行时用这种代码替换绑定变量。绑定变量部分正在工作。我会查一查再给你回复。感谢
条款中
的限制(如果适用)。在这里,我在
中使用了一个子查询
,因此没有这样的限制,事实上,我刚刚将连接条件重写为半连接(希望连接和半连接没有基本限制:)。
CREATE OR REPLACE PROCEDURE "TEST_PROC"(testCodes   IN inputcodeArr,
                                        timeHorizon IN NUMBER,
                                        p_recordset OUT SYS_REFCURSOR) AS
   l_sql LONG := 'SELECT t.a, t.b, t.c FROM test t WHERE';
BEGIN
   -- filter #1
   IF testCodes IS NOT NULL THEN
      l_sql := l_sql || ' t.name IN (SELECT pc FROM TABLE(:filter1))';
   ELSE
      l_sql := l_sql || ' :filter1 IS NULL';
   END IF;
   -- filter #2
   IF timeHorizon IS NOT NULL THEN
      l_sql := l_sql || ' AND t.horizon = :filter2';
   ELSE
      l_sql := l_sql || ' AND :filter2 IS NULL';
   END IF;
   -- open cursor
   OPEN p_recordset FOR l_sql USING testCodes, timeHorizon;
END TEST_PROC;
/