如何从选择查询向 PL/SQL 循环提供值
Posted
技术标签:
【中文标题】如何从选择查询向 PL/SQL 循环提供值【英文标题】:How to supply values to a PL/SQL loop from a select query 【发布时间】:2015-04-22 10:33:54 【问题描述】:各位朋友,我有一个 perl 脚本,它通过 sqlplus 与 oracle Db 交互。在这里,我正在运行两个查询。第一个查询从视图中获取特定日期的序列号并写入文件。
SELECT DISTINCT serialnumber FROM VOUCHERHISTORYCLIENTV WHERE at LIKE '&1'; (where &1 = $date)
然后 perl 循环读取此文件,逐行获取序列号并运行以下查询,将数据写入另一个文件,然后由以下 perl 代码处理。现在这些序列号太大了,所以循环的每次迭代都连接到 sqlplus 运行查询并生成输出然后断开连接。这就是为什么它花费了太多时间。
SELECT * FROM VSOWNERUPE07.VOUCHERHISTORYCLIENTV WHERE serialnumber = '&1'; (where &1 = $serialnumber)
有什么方法不需要我一次又一次地连接和断开 sqlplus 会话?我最终想要的是附加在 unix 文件上的每个迭代的第二个查询的结果..以便我的 perl 脚本可以格式化它们。我猜 pl/sql 循环可以做到这一点……但我从来没有在 pl/sql 上工作过……你能帮帮我吗?
PS:我不能在这里使用 DBD::oracle,因为我在 solaris 机器上安装这个模块时遇到了太多问题,因为这个服务器是第 3 方服务器,所以我无法对此进行任何更改
更新:1
我尝试了以下两个程序并针对表有 600 万条记录的特定日期运行。但是查询一直在运行并且即使在 2 天后也没有产生任何输出......
程序#1:
DECLARE
CURSOR CUR1 IS
SELECT DISTINCT serialnumber FROM VSOWNERUPE07.VOUCHERHISTORYCLIENTV WHERE at LIKE '&1';
CURSOR CUR2(p_ser_num NUMBER) IS
SELECT serialnumber, state, at as time1, operatorid FROM VSOWNERUPE07.VOUCHERHISTORYCLIENTV WHERE serialnumber = p_ser_num;
BEGIN
FOR l_cur1 IN CUR1
LOOP
NULL;
FOR l_cur2 IN CUR2(l_cur1.serialnumber)
LOOP
DBMS_OUTPUT.PUT_LINE(l_cur2.serialnumber||' '||l_cur2.state ||' '||l_cur2.time1 ||' '||l_cur2.operatorid );
END LOOP;
END LOOP;
END;
/
quit;
程序#2:
DECLARE
CURSOR CUR1 IS
SELECT DISTINCT serialnumber FROM VSOWNERUPE07.VOUCHERHISTORYCLIENTV WHERE at LIKE '&1';
TYPE t_tab1 IS TABLE OF CUR1%ROWTYPE;
l_tab1 t_tab1;
CURSOR CUR2(p_ser_num NUMBER) IS
SELECT serialnumber, state, at as time1, operatorid FROM VSOWNERUPE07.VOUCHERHISTORYCLIENTV WHERE serialnumber = p_ser_num;
TYPE t_tab2 IS TABLE OF CUR2%ROWTYPE;
l_tab2 t_tab2;
BEGIN
OPEN CUR1;
LOOP
FETCH CUR1 BULK COLLECT INTO l_tab1;
EXIT WHEN CUR1%NOTFOUND;
END LOOP;
CLOSE CUR1;
FOR i in 1..l_tab1.COUNT
LOOP
OPEN CUR2(l_tab1(i).serialnumber);
LOOP
FETCH CUR2 BULK COLLECT INTO l_tab2;
EXIT WHEN CUR2%NOTFOUND;
END LOOP;
CLOSE CUR2;
for j in 1..l_tab2.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(l_tab2(j).serialnumber||' '||l_tab2(j).state ||' '||l_tab2(j).time1 ||' '||l_tab2(j).operatorid );
END LOOP;
END LOOP;
END;
/
quit;
我可以改进上述程序还是有其他方法可以为我们完成这项工作?请帮忙。
【问题讨论】:
您能否从代码中删除 DBMS_OUTPUT(整个循环)并运行并检查时间? 另外,为了安全,请在 FETCH 中使用 LIMIT 子句 - “FETCH CUR2 BULK COLLECT INTO l_tab2 限制 10000" 您尝试过使用索引吗?当然,以上所有内容都假设 VOUCHERHISTORYCLIENTV 是您提到的表而不是视图 对不起,我犯了一个错误。我忘了提到 VOUCHERHISTORYCLIENTV 是一个视图而不是表...它从另外 2 个视图和其他 6 到 7 个表中获取数据...同时我将根据您的建议工作并让您知道结果... Man.. 这意味着您需要优化底层视图查询 - 递归地 - 直到您到达所有视图 - 因为这是实际触发的内容。(SELECT * FROM MY_VIEW == THE QUERY OF风景)。你有很多分析要做:) 【参考方案1】:一次,您可以用这个单一的查询替换 2 个查询:
SELECT * FROM VSOWNERUPE07.VOUCHERHISTORYCLIENTV
WHERE serialnumber IN (SELECT DISTINCT serialnumber FROM
VOUCHERHISTORYCLIENTV WHERE at LIKE '&1');
并写入输出到您的最终文件。
您发现这有什么问题吗?
更新1
首先,将它分成 2 个不同的查询几乎从来都不是更好的解决方案。SQL 上下文切换会杀死你。
分别在两个表的serialnumber
和at
列上建立索引并尝试使用单个查询。
如果即使速度很慢,您也可以尝试 UPDATE2。
更新2
如果有太多记录是问题所在,则使用批量处理。 这个过程有点乏味,但旨在处理这类情况。
如何进行批量处理的一些示例是here、here 和here。
为此,您可能必须编写一个 PLSQL 过程。 在该过程中,从您的第一个查询中将所有数据批量收集到一个可变数组中。 现在使用此变量运行第二个选择查询并收集您的数据。
下一步 您可以将这些序列号放入,比如说另一个 v 数组并将其写入文件 或 创建一个新的单列表并将其写入此新表。 然后将其写入文件/分别从 perl 查询这个新表。 注1: 如果您使用单独的表,请在每次运行后截断它;并在其上建立索引 注意 2:如果您不知道我在说什么,请先查看这些链接。
【讨论】:
谢谢.. 是的,我尝试了这个查询,但是由于 Db 视图太大并且包含数百万条记录.. 查询被挂起并且无法产生输出。这就是为什么我把它分成两部分...... 感谢您的帮助.. 我浏览了链接并试图找出解决方案,但又卡住了.. 我已经在原始问题中解释了我的问题。你能帮忙吗..以上是关于如何从选择查询向 PL/SQL 循环提供值的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Dbms_output.put_line(''); 中使用 <br> 标签在 PL/SQL 甲骨文中?