从PL/SQL变量一次一个地添加元素到集合变量?

Rob*_*t N 2 oracle plsql oracle11g

我有一个用SQL Server编写的T-SQL例程.我们正在迁移到Oracle,因此我尝试将其移植到PL/SQL.这是T-SQL例程(简化); 请注意使用表值变量,在Oracle中,它将成为"嵌套表"类型的PL/SQL变量.我的问题的主要内容是在PL/SQL中使用这种"集合"对象的最佳方法.移植代码中的几个操作(下面的第二个代码示例)非常笨拙,在SQL Server原始版本中它们似乎更容易:

DECLARE @MyValueCollection TABLE( value VARCHAR(4000) );
DECLARE @valueForThisRow VARCHAR(4000);

DECLARE @dataItem1Val INT, @dataItem2Val INT, @dataItem3Val INT, @dataItem4Val INT;
DECLARE theCursor CURSOR FAST_FORWARD FOR
  SELECT DataItem1, DataItem2, DataItem3, DataItem4 FROM DataTable;

OPEN theCursor;

FETCH NEXT FROM theCursor INTO @dataItem1Val, @dataItem2Val, @dataItem3Val, @dataItem4Val;

WHILE @@FETCH_STATUS = 0
BEGIN
  -- About 50 lines of logic that evaluates @dataItem1Val, @dataItem2Val, @dataItem3Val, @dataItem4Val and constructs @valueForThisRow
  SET @valueForThisRow = 'whatever';

  -- !!! This is the row that seems to have no natural Oracle equivalent
  INSERT INTO @MyValueCollection VALUES(@valueForThisRow);  

  FETCH NEXT FROM theCursor INTO @dataItem1Val, @dataItem2Val, @dataItem3Val, @dataItem4Val;
END;

CLOSE theCursor;
DEALLOCATE theCursor;

-- !!! output all the results; this also seems harder than it needs to be in Oracle
SELECT * FROM @MyValueCollection;
Run Code Online (Sandbox Code Playgroud)

我已经能够移植几乎所有东西,但在两个地方(参见代码中的注释),逻辑比旧的SQL Server方式复杂得多,我想知道在Oracle中是否会有更优雅的东西那种躲避我的方式:

set serveroutput on; -- needed for DBMS_OUTPUT; see below

DECLARE
  TYPE StringList IS TABLE OF VARCHAR2(4000);
  myValueCollection StringList;
  dummyTempCollection StringList; -- needed for my kludge; see below
  valueForThisRow VARCHAR2(4000);
BEGIN
  -- build all the sql statements
  FOR c IN (
    SELECT DataItem1, DataItem2, DataItem3, DataItem4 FROM DataTable;
  )
  LOOP
    -- About 50 lines of logic that evaluates c.DataItem1, c.DataItem2, c.DataItem3, c.DataItem4 and constructs valueForThisRow
    valueForThisRow := 'whatever';

    -- This seems way harder than it should be; I would rather not need an extra dummy collection
    SELECT valueForThisRow BULK COLLECT INTO dummyTempCollection FROM dual;    -- overwrites content of dummy temp
    myValueCollection := myValueCollection MULTISET UNION dummyTempCollection; -- merges into main collection

  END LOOP;

  -- output all the results... again, there's no shorter/easier/more-compact/single-line equivalent?
  IF myValueCollection.COUNT > 0
  THEN
    FOR indx IN myValueCollection.FIRST .. myValueCollection.LAST
    LOOP
       DBMS_OUTPUT.PUT_LINE(myValueCollection(indx));
    END LOOP;
  END IF;
END;
/
Run Code Online (Sandbox Code Playgroud)

在此先感谢您的帮助!

Jus*_*ave 7

就个人而言,我将采用"50行逻辑",将其移动到您在SQL语句中调用的函数中,然后执行一个简单的BULK COLLECT操作将数据加载到本地集合中.

假设您确实希望逐个元素地将数据加载到集合中,您可以简化加载集合的代码

DECLARE
  TYPE StringList IS TABLE OF VARCHAR2(4000);
  myValueCollection StringList := StringList();
  valueForThisRow VARCHAR2(4000);
BEGIN
  -- build all the sql statements
  FOR c IN (
    SELECT DataItem1, DataItem2, DataItem3, DataItem4 FROM DataTable;
  )
  LOOP
    -- About 50 lines of logic that evaluates c.DataItem1, c.DataItem2, c.DataItem3, c.DataItem4 and constructs valueForThisRow
    valueForThisRow := 'whatever';

    myValueCollection.extend();
    myValueCollection( myValueCollection.count ) := valueForThisRow;
  END LOOP;

  -- output all the results... again, there's no shorter/easier/more-compact/single-line equivalent?
  IF myValueCollection.COUNT > 0
  THEN
    FOR indx IN myValueCollection.FIRST .. myValueCollection.LAST
    LOOP
       DBMS_OUTPUT.PUT_LINE(myValueCollection(indx));
    END LOOP;
  END IF;
END;
/
Run Code Online (Sandbox Code Playgroud)

如果将集合声明为关联数组,则可以避免调用extend以增加集合的大小.如果您知道要加载到集合中的元素数量,则可以将其传递给extend循环外的单个调用.潜在地,您还可以消除valueForThisRow局部变量,并仅对集合中的元素进行操作.

至于处理集合的代码,你真正想做的是什么?生产代码写入dbms_output并期望任何人在正常处理期间都能看到输出是非常不寻常的.这将影响您编写该代码的方式.假设你的意图真的只是打电话dbms_output,知道通常会将数据发送到以太网

FOR indx IN 1 .. myValueCollection.count
LOOP
  dbms_output.put_line( myValueCollection(indx) );
END LOOP;
Run Code Online (Sandbox Code Playgroud)

当您拥有密集集合时(所有索引在1和count集合之间存在且具有值),这都适用.如果你可能有一个稀疏集合,你会想使用FIRST,NEXT以及LAST在一个循环,但是这是一个有点更多的代码.