DBMS_SQL.NUMBER_TABLE 内部更新语句抛出无效数据类型错误
DBMS_SQL.NUMBER_TABLE inside update statement throwing invalid data type error
为什么 select 有效但更新会抛出无效数据?
DECLARE
L_NUMBER NUMBER;
L_NUMBER_TABLE DBMS_SQL.NUMBER_TABLE;
L_LAST_PRINTED_DATE DATE := SYSDATE;
BEGIN
L_NUMBER_TABLE(0) := 1033000;
-- THIS WORKS
SELECT TB.COLUMN_VALUE
INTO L_NUMBER
FROM TABLE(L_NUMBER_TABLE) TB;
-- THIS DOES NOT WORK
-- ERROR AT LINE 1
-- ORA-00902: INVALID DATATYPE
-- ORA-06512: AT LINE 13
UPDATE SCHEMA.REAL_NUMBER_TABLE
SET REAL_NUMBER_DATE = L_LAST_PRINTED_DATE
WHERE EXISTS ( SELECT TB.COLUMN_VALUE
FROM TABLE( L_NUMBER_TABLE ) TB
WHERE TB.COLUMN_VALUE = REAL_NUMBER_COLUMN );
END;
我正在尝试遍历游标并更新在游标内找到的主序列的最后打印日期。我尝试通过游标循环,但是当我 return 将游标发送到客户端时,它会抛出索引错误。所以我被迫制作了两个游标,一个循环遍历,一个循环 return。我的目标是了解什么是保存集合以更新 table 的最简单和最可维护的方法。
DECLARE
CURSOR L_ORIGINAL_CURSOR IS SELECT ...
L_CURSOR_COLUMN_1 PLS_INTEGER;
L_CURSOR_COLUMN_2 PLS_INTEGER;
L_CURSOR_COLUMN_3 PLS_INTEGER;
-- Keep adding or removing the number of columns to match...1/2 09182019515PM
-- L_CURSOR_COLUMN_4 PLS_INTEGER;
L_NUMBER_TABLE DBMS_SQL.NUMBER_TABLE;
L_COUNTER PLS_INTEGER;
L_LAST_PRINTED_DATE DATE := SYSDATE;
BEGIN
OPEN L_ORIGINAL_CURSOR;
LOOP
-- Keep adding or removing the number of columns to match... 2/2 09182019515PM
FETCH L_ORIGINAL_CURSOR INTO L_CURSOR_COLUMN_1, L_CURSOR_COLUMN_2, L_CURSOR_COLUMN_3; -- , L_CURSOR_COLUMN_4;
IF L_ORIGINAL_CURSOR%NOTFOUND THEN
EXIT;
END IF;
IF L_ORIGINAL_CURSOR%FOUND THEN
-- CURRENT SOLUTION IS TO UPDATE HERE BOUNCING BETWEEN SQL AND PLSQL ENGINES
-- UPDATE ....
-- WANTED IMPLEMENTATION
L_COUNTER := L_COUNTER + 1;
END IF;
-- WANTED IMPLEMENTATION STORE PK IN MY COLLECTION
L_NUMBER_TABLE(L_COUNTER) := L_CURSOR_COLUMN_1;
END LOOP;
-- IF COLLECTION IS BIGGER THAN 0
IF L_NUMBER_TABLE.COUNT > 0 THEN
-- SCRIPT BREAKS HERE
UPDATE ...
SET ... = L_LAST_PRINTED_DATE
WHERE EXISTS ( SELECT TB.COLUMN_VALUE
FROM TABLE(L_NUMBER_TABLE) TB
WHERE TB.COLUMN_VALUE = ... );
END IF;
CLOSE L_ORIGINAL_CURSOR;
OPEN L_CURSOR FOR SELECT ...
END SP_GET_PM_WORK_ORDERS;
/
SHOW ERRORS;
目前内联单个 sql 更新语句有效,但需要在 SQL 和 PLSQL 引擎之间进行多个引擎跳转。为什么我可以做 selects 但不能更新我的号码 table?
数据库版本:12.1.0.2.0
DBMS_SQL.NUMBER_TABLE
是一个 PL/SQL 关联数组,在 DBMS_SQL
包中定义为:
TYPE number_table IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
这是一个 PL/SQL 数据类型,不应该在 SQL 语句中工作(我还没有弄清楚为什么你的第一个语句有效)。
如果您想要在 SQL 中工作的数据类型,那么您需要使用集合(没有 INDEX BY
;也称为嵌套 table 数据类型):
CREATE TYPE number_table IS TABLE OF NUMBER;
或者定长VARRAY
:
CREATE TYPE number_array IS VARRAY(10) OF NUMBER;
例如:
Oracle 设置:
CREATE TABLE real_number_table ( real_number_column, real_number_date ) AS
SELECT 1033000, DATE '2019-01-01' FROM DUAL;
CREATE TYPE number_table IS TABLE OF NUMBER;
PL/SQL 语句 1:
然后您的 SQL 语句将使用集合数据类型:
DECLARE
L_NUMBER NUMBER;
L_NUMBER_TABLE NUMBER_TABLE;
L_LAST_PRINTED_DATE DATE := SYSDATE;
BEGIN
L_NUMBER_TABLE := NUMBER_TABLE();
L_NUMBER_TABLE.EXTEND;
L_NUMBER_TABLE( L_NUMBER_TABLE.COUNT ) := 1033000;
UPDATE REAL_NUMBER_TABLE
SET REAL_NUMBER_DATE = L_LAST_PRINTED_DATE
WHERE EXISTS (
SELECT TB.COLUMN_VALUE
FROM TABLE( L_NUMBER_TABLE ) TB
WHERE TB.COLUMN_VALUE = REAL_NUMBER_COLUMN
);
END;
/
然后:
SELECT * FROM real_number_table;
输出:
REAL_NUMBER_COLUMN | REAL_NUMBER_DATE
-----------------: | :---------------
1033000 | 09-OCT-19
PL/SQL 语句 2:
或者您可以简化它并使用 MEMBER OF
运算符(这仅适用于集合数据类型,而不适用于 VARRAY
s):
DECLARE
L_NUMBER NUMBER;
L_NUMBER_TABLE NUMBER_TABLE := NUMBER_TABLE( 1033000 );
L_LAST_PRINTED_DATE DATE := SYSDATE + 1;
BEGIN
UPDATE REAL_NUMBER_TABLE
SET REAL_NUMBER_DATE = L_LAST_PRINTED_DATE
WHERE REAL_NUMBER_COLUMN MEMBER OF L_NUMBER_TABLE;
END;
/
然后:
SELECT * FROM real_number_table;
输出:
REAL_NUMBER_COLUMN | REAL_NUMBER_DATE
-----------------: | :---------------
1033000 | 10-OCT-19
db<>fiddle here
你最终的 PL/SQL 匿名块可以重写为:
DECLARE
L_NUMBER_TABLE NUMBER_TABLE;
L_LAST_PRINTED_DATE DATE := SYSDATE;
BEGIN
SELECT column1
BULK COLLECT INTO L_NUMBER_TABLE
FROM your_table; -- as per L_ORIGINAL_CURSOR
IF L_NUMBER_TABLE.COUNT > 0 THEN
UPDATE other_table
SET date_column = L_LAST_PRINTED_DATE
WHERE number_column MEMBER OF L_NUMBER_TABLE;
END IF;
END;
/
MT0 提供了有效的解决方案。如果 REAL_NUMBER_COLUMN
是 unique/primary 键,那么您也可以使用这个(相当未知的)版本:
UPDATE (
SELECT REAL_NUMBER_COLUMN, REAL_NUMBER_DATE
FROM SCHEMA.REAL_NUMBER_TABLE
JOIN TABLE(L_NUMBER_TABLE) ON COLUMN_VALUE = REAL_NUMBER_COLUMN
)
SET REAL_NUMBER_DATE = L_LAST_PRINTED_DATE;
为什么 select 有效但更新会抛出无效数据?
DECLARE
L_NUMBER NUMBER;
L_NUMBER_TABLE DBMS_SQL.NUMBER_TABLE;
L_LAST_PRINTED_DATE DATE := SYSDATE;
BEGIN
L_NUMBER_TABLE(0) := 1033000;
-- THIS WORKS
SELECT TB.COLUMN_VALUE
INTO L_NUMBER
FROM TABLE(L_NUMBER_TABLE) TB;
-- THIS DOES NOT WORK
-- ERROR AT LINE 1
-- ORA-00902: INVALID DATATYPE
-- ORA-06512: AT LINE 13
UPDATE SCHEMA.REAL_NUMBER_TABLE
SET REAL_NUMBER_DATE = L_LAST_PRINTED_DATE
WHERE EXISTS ( SELECT TB.COLUMN_VALUE
FROM TABLE( L_NUMBER_TABLE ) TB
WHERE TB.COLUMN_VALUE = REAL_NUMBER_COLUMN );
END;
我正在尝试遍历游标并更新在游标内找到的主序列的最后打印日期。我尝试通过游标循环,但是当我 return 将游标发送到客户端时,它会抛出索引错误。所以我被迫制作了两个游标,一个循环遍历,一个循环 return。我的目标是了解什么是保存集合以更新 table 的最简单和最可维护的方法。
DECLARE
CURSOR L_ORIGINAL_CURSOR IS SELECT ...
L_CURSOR_COLUMN_1 PLS_INTEGER;
L_CURSOR_COLUMN_2 PLS_INTEGER;
L_CURSOR_COLUMN_3 PLS_INTEGER;
-- Keep adding or removing the number of columns to match...1/2 09182019515PM
-- L_CURSOR_COLUMN_4 PLS_INTEGER;
L_NUMBER_TABLE DBMS_SQL.NUMBER_TABLE;
L_COUNTER PLS_INTEGER;
L_LAST_PRINTED_DATE DATE := SYSDATE;
BEGIN
OPEN L_ORIGINAL_CURSOR;
LOOP
-- Keep adding or removing the number of columns to match... 2/2 09182019515PM
FETCH L_ORIGINAL_CURSOR INTO L_CURSOR_COLUMN_1, L_CURSOR_COLUMN_2, L_CURSOR_COLUMN_3; -- , L_CURSOR_COLUMN_4;
IF L_ORIGINAL_CURSOR%NOTFOUND THEN
EXIT;
END IF;
IF L_ORIGINAL_CURSOR%FOUND THEN
-- CURRENT SOLUTION IS TO UPDATE HERE BOUNCING BETWEEN SQL AND PLSQL ENGINES
-- UPDATE ....
-- WANTED IMPLEMENTATION
L_COUNTER := L_COUNTER + 1;
END IF;
-- WANTED IMPLEMENTATION STORE PK IN MY COLLECTION
L_NUMBER_TABLE(L_COUNTER) := L_CURSOR_COLUMN_1;
END LOOP;
-- IF COLLECTION IS BIGGER THAN 0
IF L_NUMBER_TABLE.COUNT > 0 THEN
-- SCRIPT BREAKS HERE
UPDATE ...
SET ... = L_LAST_PRINTED_DATE
WHERE EXISTS ( SELECT TB.COLUMN_VALUE
FROM TABLE(L_NUMBER_TABLE) TB
WHERE TB.COLUMN_VALUE = ... );
END IF;
CLOSE L_ORIGINAL_CURSOR;
OPEN L_CURSOR FOR SELECT ...
END SP_GET_PM_WORK_ORDERS;
/
SHOW ERRORS;
目前内联单个 sql 更新语句有效,但需要在 SQL 和 PLSQL 引擎之间进行多个引擎跳转。为什么我可以做 selects 但不能更新我的号码 table?
数据库版本:12.1.0.2.0
DBMS_SQL.NUMBER_TABLE
是一个 PL/SQL 关联数组,在 DBMS_SQL
包中定义为:
TYPE number_table IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
这是一个 PL/SQL 数据类型,不应该在 SQL 语句中工作(我还没有弄清楚为什么你的第一个语句有效)。
如果您想要在 SQL 中工作的数据类型,那么您需要使用集合(没有 INDEX BY
;也称为嵌套 table 数据类型):
CREATE TYPE number_table IS TABLE OF NUMBER;
或者定长VARRAY
:
CREATE TYPE number_array IS VARRAY(10) OF NUMBER;
例如:
Oracle 设置:
CREATE TABLE real_number_table ( real_number_column, real_number_date ) AS
SELECT 1033000, DATE '2019-01-01' FROM DUAL;
CREATE TYPE number_table IS TABLE OF NUMBER;
PL/SQL 语句 1:
然后您的 SQL 语句将使用集合数据类型:
DECLARE
L_NUMBER NUMBER;
L_NUMBER_TABLE NUMBER_TABLE;
L_LAST_PRINTED_DATE DATE := SYSDATE;
BEGIN
L_NUMBER_TABLE := NUMBER_TABLE();
L_NUMBER_TABLE.EXTEND;
L_NUMBER_TABLE( L_NUMBER_TABLE.COUNT ) := 1033000;
UPDATE REAL_NUMBER_TABLE
SET REAL_NUMBER_DATE = L_LAST_PRINTED_DATE
WHERE EXISTS (
SELECT TB.COLUMN_VALUE
FROM TABLE( L_NUMBER_TABLE ) TB
WHERE TB.COLUMN_VALUE = REAL_NUMBER_COLUMN
);
END;
/
然后:
SELECT * FROM real_number_table;
输出:
REAL_NUMBER_COLUMN | REAL_NUMBER_DATE -----------------: | :--------------- 1033000 | 09-OCT-19
PL/SQL 语句 2:
或者您可以简化它并使用 MEMBER OF
运算符(这仅适用于集合数据类型,而不适用于 VARRAY
s):
DECLARE
L_NUMBER NUMBER;
L_NUMBER_TABLE NUMBER_TABLE := NUMBER_TABLE( 1033000 );
L_LAST_PRINTED_DATE DATE := SYSDATE + 1;
BEGIN
UPDATE REAL_NUMBER_TABLE
SET REAL_NUMBER_DATE = L_LAST_PRINTED_DATE
WHERE REAL_NUMBER_COLUMN MEMBER OF L_NUMBER_TABLE;
END;
/
然后:
SELECT * FROM real_number_table;
输出:
REAL_NUMBER_COLUMN | REAL_NUMBER_DATE -----------------: | :--------------- 1033000 | 10-OCT-19
db<>fiddle here
你最终的 PL/SQL 匿名块可以重写为:
DECLARE
L_NUMBER_TABLE NUMBER_TABLE;
L_LAST_PRINTED_DATE DATE := SYSDATE;
BEGIN
SELECT column1
BULK COLLECT INTO L_NUMBER_TABLE
FROM your_table; -- as per L_ORIGINAL_CURSOR
IF L_NUMBER_TABLE.COUNT > 0 THEN
UPDATE other_table
SET date_column = L_LAST_PRINTED_DATE
WHERE number_column MEMBER OF L_NUMBER_TABLE;
END IF;
END;
/
MT0 提供了有效的解决方案。如果 REAL_NUMBER_COLUMN
是 unique/primary 键,那么您也可以使用这个(相当未知的)版本:
UPDATE (
SELECT REAL_NUMBER_COLUMN, REAL_NUMBER_DATE
FROM SCHEMA.REAL_NUMBER_TABLE
JOIN TABLE(L_NUMBER_TABLE) ON COLUMN_VALUE = REAL_NUMBER_COLUMN
)
SET REAL_NUMBER_DATE = L_LAST_PRINTED_DATE;