使用Oracle表类型执行动态本机SQL时出现无效标识符错误
我们已经创建了Oracle表类型。我们已经创建了一个数组。这样做是因为我们不知道可以得到多少值,这对于sql查询中的IN子句来说可能太多了 --代码片段----- 以上所有测试都是编译的,当您使用很少的testCode值运行TEST_PROC过程时,它将失败,并出现testCode标识符无效的错误 在该过程中,在我的控制台中打印的最终sql语句是正确的,当您在过程中以静态sql语句的形式运行它时,它不会出现任何错误。但在动态sql中,它失败了。 我尝试使用DYNAMIC_SQL包执行,但结果是相同的错误。 此外,我还尝试将其作为“table(testCodes)”的绑定变量。这也失败了 请给出建议。您正在使用,因此您必须告诉Oracle哪个单词是标识符,哪个单词是变量 考虑以下直接在SQLPlus中运行的语句:使用Oracle表类型执行动态本机SQL时出现无效标识符错误,sql,oracle,dynamic,types,procedure,Sql,Oracle,Dynamic,Types,Procedure,我们已经创建了Oracle表类型。我们已经创建了一个数组。这样做是因为我们不知道可以得到多少值,这对于sql查询中的IN子句来说可能太多了 --代码片段----- 以上所有测试都是编译的,当您使用很少的testCode值运行TEST_PROC过程时,它将失败,并出现testCode标识符无效的错误 在该过程中,在我的控制台中打印的最终sql语句是正确的,当您在过程中以静态sql语句的形式运行它时,它不会出现任何错误。但在动态sql中,它失败了。 我尝试使用DYNAMIC_SQL包执行,但结果是相
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;
/