Oracle 将PL/SQL变量中的一个元素一次添加到集合变量中?

Oracle 将PL/SQL变量中的一个元素一次添加到集合变量中?,oracle,plsql,oracle11g,Oracle,Plsql,Oracle11g,我有一个用T-SQL为SQL Server编写的例程。我们正在迁移到Oracle,所以我尝试将其移植到PL/SQL。下面是T-SQL例程(简化);注意表值变量的使用,在Oracle中,它将成为“嵌套表”类型的PL/SQL变量。我的问题的重点是在PL/SQL中处理此类“集合”对象的最佳方法。移植代码(下面的第二个代码示例)中的一些操作相当笨拙,在SQL Server原始版本中,这些操作似乎简单得多: DECLARE @MyValueCollection TABLE( value VARCHAR(4

我有一个用T-SQL为SQL Server编写的例程。我们正在迁移到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;
我已经能够移植几乎所有的东西,但在两个地方(请参见代码中的注释),逻辑比旧的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;
/
提前感谢您的帮助

就我个人而言,我会将“50行逻辑”移动到您在SQL语句中调用的函数中,然后执行一个简单的
批量收集
,将数据加载到您的本地收集中

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

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;
/
如果将集合声明为关联数组,则可以避免调用
extend
来增加集合的大小。如果知道要加载到集合中的元素数量,可以将其传递给循环外部的单个
extend
调用。您还可以消除该行的
局部变量,只对集合中的元素进行操作

至于处理集合的代码,您真正想做的是什么?生产代码写入
dbms\u输出
,并期望任何人都能在正常处理过程中看到输出,这是非常罕见的。这将影响您编写代码的方式。假设您的意图是只调用
dbms\u output
,知道这通常会将数据发送到以太中

FOR indx IN 1 .. myValueCollection.count
LOOP
  dbms_output.put_line( myValueCollection(indx) );
END LOOP;

当您有一个密集的集合(集合的
count
之间的所有索引都存在并具有值)时,这种方法就有效了。如果您可能有一个稀疏的集合,那么您可能希望在循环中使用
FIRST
NEXT
、和
LAST
,但这需要更多的代码。

也许一个临时表可以帮到您。明白你说的“例行公事”是什么意思吗?存储过程?另外,您在哪里输出最终选择?到一个.net应用程序?这个例程每次调用时会选择并插入多少行?@BobJarvis:大约250行。在这种情况下,我会尝试将50行代码直接放入SELECT中(你可以对case表达式做很多事情-可能足够,也可能不够,但值得研究),然后只使用INSERT…SELECT。。。将数据直接猛击到表中。YMMV。分享和享受。感谢所有有用的信息。我喜欢函数的想法,但我对端口的指示是尽可能少地更改,因此我感谢您对如何简化我所得到的内容的建议。我对dbms_输出所做的是将其假脱机到一个临时文件,然后由另一个(非Oracle)作业拾取并进一步处理。如果在这样的应用程序的生产代码中使用它有问题,请让我也知道@RobertN-写入文件通常通过
utl\u文件
完成。这将在数据库服务器上写入一个文件。我不清楚您是在尝试这样做,还是在尝试使用SQL*Plus在客户端上写入文件。哈哈,是的,Justin,这就是发生的事情!我最初使用UTL_文件编写移植版本,但需要提取生成的文件的过程在客户端,因此我更愿意在客户端编写该文件。我可以在数据库服务器上打开文件共享,但我们的安全人员希望我不要这样做。因此,这个问题的全部原因是::-)