在 Oracle PL/SQL 循环索引变量 'I' 中执行存储过程时出错 use is invalid

Error when executing stored procedure in Oracle PL/SQL loop index variable 'I' use is invalid

我对 Oracle PL/SQL 还很陌生,我正在尝试执行一个存储过程,该过程插入到 orders table 并循环将项目集合插入到order_itemtable。我创建了一个已定义的类型,但是当我尝试执行我的过程时,我得到了这个 loop index variable 'I' use is invalid.

SQL> -- Execute procedure
SQL> DECLARE
  2  order_id_ NUMBER;
  3
  4  BEGIN
  5      insert_order(p_order_id => 4, p_order_num => 'O223PS56', p_name => 'Test Test', p_email => 'test@test.co.uk', p_address => '123 Test Street', p_city => 'Newcastle Upon Tyne', p_province => 'Tyne and Wear', p_postcode => 'NE98 4TN', p_telephone => '123456789', p_total => 7.97, p_order_date => to_date('11-apr-2021', 'DD-mon-YYYY'));
  6      FOR i IN i..order_items
  7      LOOP
  8        insert_order_items(order_id_, order_items(i) => 5, order_items(i) => 2, order_items(i) => 2, order_items(i) => 3073748221, order_items(i) => 2, order_items(i) => 'Brand New', order_items(i) => 1.99, order_items(i) => 1.99);
  9       COMMIT;
 10     END LOOP;
 11  END;
 12  /
    FOR i IN i..order_items
             *
ERROR at line 6:
ORA-06550: line 6, column 14:
PLS-00364: loop index variable 'I' use is invalid
ORA-06550: line 6, column 5:
PL/SQL: Statement ignored
SQL>
CREATE OR REPLACE PROCEDURE insert_order
(
    p_order_id IN INT, p_order_num IN CHAR,
    p_name IN CHAR, p_email IN CHAR,
    p_address IN VARCHAR2, p_city IN VARCHAR2,
    p_province IN VARCHAR2, p_postcode IN VARCHAR2,
    p_telephone IN NUMBER, p_total IN NUMBER,
    p_order_date IN DATE
)
AS
BEGIN
    INSERT INTO orders(order_id, order_number, billing_name, billing_email, billing_address, billing_city, billing_province, billing_postcode, billing_telephone, billing_total, order_date)
    values(p_order_id, p_order_num, p_name, p_email, p_address, p_city, p_province, p_postcode, p_telephone, p_total, p_order_date);

    COMMIT;
END;
/

CREATE OR REPLACE PROCEDURE insert_order_items
(
    p_order_item_id IN INT, p_order_id IN INT, 
    p_product_id IN INT, p_seller_id IN INT,
    p_sub_order_number IN CHAR, p_quantity IN INT,
    p_condition IN CHAR, p_unit_price IN NUMBER, 
    p_cost_charge IN NUMBER
)
AS
BEGIN
    INSERT INTO order_item(order_item_id, order_id, product_id, seller_id, sub_order_number, quantity, condition, unit_price, cost_charge)
    values(p_order_item_id, p_order_id, p_product_id, p_seller_id, p_sub_order_number, p_quantity, p_condition, p_unit_price, p_cost_charge);
    COMMIT;
END;
/
CREATE OR REPLACE TYPE order_items_collection as object(
    order_id int, product_id int,
    seller_id int, sub_order_number char(10),
    quantity int, condition char(100),
    unit_price number, cost_charge number
);
/

CREATE OR REPLACE TYPE order_items is table of order_items_collection;
/

您正在声明一个过程,并将其作为函数调用。这就是它不起作用的原因。

试试这个:

DECLARE
order_id_ NUMBER;

BEGIN
    -- note here that you're now assigning the return value to anything
    insert_order(p_order_id => 4, p_order_num => 'O223PS56', p_name => 'Test Test', p_email => 'test@test.co.uk', p_address => '123 Test Street', p_city => 'Newcastle Upon Tyne', p_province => 'Tyne and Wear', p_postcode => 'NE98 4TN', p_telephone => '123456789', p_total => 7.97, p_order_date => to_date('11-apr-2021', 'DD-mon-YYYY'));
    FOR i IN 1..order_items
    LOOP
      insert_order_items(order_id_, order_items(i) => 5, order_items(i) => 2, order_items(i) => 2, order_items(i) => 3073748221, order_items(i) => 2, order_items(i) => 'Brand New', order_items(i) => 1.99, order_items(i) => 1.99);
     COMMIT;
   END LOOP;
END;
/

这个:

FOR i IN i..order_items

没有意义; i 是循环变量,所以它也不能是范围。你可以这样做:

for i in order_items.FIRST..order_items.LAST

或者因为这不是稀疏数组,也许更简单:

for i in 1..order_items.COUNT loop

但是 order_items 是一个 类型 ,您不能迭代一个类型。您将迭代集合类型的 实例 。您尚未声明或填充该类型的变量。在循环中,您对该类型的引用没有意义,如果它是一个实例也不会。

据我所知,你正试图做这样的事情:

DECLARE
  order_id_ NUMBER;
  order_items_ order_items;
BEGIN
...
  -- create and populate instance of the collection type
  order_items_ := new order_items(
    order_items_collection(5, 2, 2, 3073748221, 2, 'Brand New', 1.99, 1.99)
  );

  -- iterate over the collection type
  for i in 1..order_items_.COUNT loop
    insert_order_items(
      p_order_item_id => order_id_,            -- these two values might be reversed?
      p_order_id => order_items_(i).order_id,  -- 
      p_product_id => order_items_(i).product_id,
      p_seller_id => order_items_(i).seller_id,
      p_sub_order_number => order_items_(i).sub_order_number,
      p_quantity => order_items_(i).quantity,
      p_condition => order_items_(i).condition,
      p_unit_price => order_items_(i).unit_price,
      p_cost_charge => order_items_(i).cost_charge
    );
  end loop;
END;
/

db<>fiddle demo

这将允许您插入多个值而无需重复过程调用,例如通过更改:

  -- create and populate instance of the collection type
  order_items_ := new order_items(
    order_items_collection(5, 2, 2, 3073748221, 2, 'Brand New', 1.99, 1.99)
  );

  -- create and populate instance of the collection type
  order_items_ := new order_items(
    order_items_collection(5, 2, 2, 3073748221, 2, 'Brand New', 1.99, 1.99),
    order_items_collection(6, 3, 4, 3073748222, 1, 'Used', 19.99, 19.99),
    order_items_collection(7, 8, 9, 3073748223, 7, 'Refurb', 9.99, 9.99)
  );

(尽管从中你可以看出你的对象命名有点混乱)。

使用硬编码值似乎并不比将这些值硬编码到三个过程调用中更容易或更简单,但也许您的目标是从其他地方传递集合,例如前端应用程序.或者您可能想将集合传递到您的过程中,并让它遍历集合并执行插入。目前,这两个过程似乎只是让事情变得有点复杂,并阻止您进行批量插入。

在过程中提交通常也不是一个好主意,尤其是您在循环中为相关数据调用的过程。如果第三个项目插入失败,则您已经提交了订单和前两个项目的插入,因此您处于不一致状态。看起来所有这些插入都应该插入到相同的事务中,并作为一个单元提交(或回滚)。