Oracle 存储过程 - 创建游标后我可以清空临时表吗
Posted
技术标签:
【中文标题】Oracle 存储过程 - 创建游标后我可以清空临时表吗【英文标题】:Oracle Stored Procedure - Can I empty temp table after cursor is created 【发布时间】:2020-01-27 14:17:20 【问题描述】:我有一个 Web 服务 API,它使用项目 ID 列表作为输入参数和数据表作为输出参数(以及与此问题无关的其他参数)。该 API 调用包内的 Oracle 存储过程来获取输出数据表的内容。
存储过程循环遍历每个项目 ID 并为其确定结果。然后它使用一个临时表来存储每个项目 ID(项目 ID、结果、sysdate)的结果。最后,使用游标查询这张临时表并得到结果。
我的问题是,随着时间的推移,这个数据表的内容变得太大(数百万条记录)。我知道我可以进行清理过程,但想知道在创建光标后删除内容是否可以接受。
这是网络服务 API 和存储过程的加水版:
public static EnumGlobal.Errorcode GetOutcomeByItem(string itemIDs, out DataTable dtOutcome, ...)
OracleDbContext dbContext = new OracleDbContext();
List<OracleParameter> spParams = new List<OracleParameter>();
DataSet dsOutcome = new DataSet();
...
try
spParams.Add(new OracleParameter("IPSITEMIDS", OracleDbType.Varchar2, itemIDs, ParameterDirection.Input));
...
spParams.Add(new OracleParameter("CUR_OUT", OracleDbType.RefCursor, ParameterDirection.Output));
try
dbContext.Open();
dbContext.ExecuteStoredProcedure("PKGSOMEQUERY.USPGETOUTCOMEBYITEM", spParams, ref dsOutcome);
PROCEDURE USPGETOUTCOMEBYITEM
(
IPSITEMIDS VARCHAR2,
...
CUR_OUT OUT GETDATACURSOR
)
IS
LVSQUERY VARCHAR2(4000):='';
V_OUTCOME VARCHAR2(5);
V_NEWITEMSLIST VARCHAR2(4000) := REPLACE(IPSITEMIDS, '''', '');
CURSOR cur IS
SELECT REGEXP_SUBSTR(V_NEWITEMSLIST, '[^,]+', 1, LEVEL) V_NEWITEM2 FROM DUAL CONNECT BY instr(V_NEWITEMSLIST, ',',1, LEVEL -1) > 0;
BEGIN
-- Loop thorugh each ITEM ID and determine outcome, add ITEM ID and OUTCOME to temp table
FOR rec IN cur LOOP
V_NEWITEM := rec.V_NEWITEM2;
...
-- Determine V_OUTCOME
...
INSERT INTO TEMPOUTCOME
(
ITEMID,
OUTCOME,
ORIGINDATE
)
VALUES
(
V_NEWITEM,
V_OUTCOME,
SYSDATE
);
COMMIT;
END LOOP;
LVSQUERY:='SELECT ITEMID, OUTCOME, ORIGINDATE FROM TEMPOUTCOME WHERE ITEMID IN (' || IPSITEMIDS || ')';
OPEN CUR_OUT FOR LVSQUERY;
COMMIT;
-- Can I do this?
-- Delete from temp table all item IDs used in this session, in one shot
-- DELETE FROM TEMPOUTCOME WHERE ITEMID IN (select REGEXP_SUBSTR(IPSITEMIDS, '\''(.*?)\''(?:\,)?', 1, LEVEL, NULL, 1) FROM dual CONNECT BY LEVEL <= REGEXP_COUNT(IPSITEMIDS, '''(?: +)?(\,)(?: +)?''', 1) + 1);
EXCEPTION WHEN OTHERS THEN
PKGHANDLEERROR.USPHANDLEERROR('USPGETOUTCOMEBYITEM', LVIERRORCODE);
OPIERRORCODE:=LVIERRORCODE;
END USPGETOUTCOMEBYITEM;
【问题讨论】:
您实际上是从TEMPOUTCOME
以外的表中读取的吗?看起来你根本不需要那个临时表。
“Determine V_OUTCOME”部分查询一组不同的表以确定每个 ITEMID 的 OUTCOME 是什么。我需要将 ITEMDID-OUTCOME-DATE 记录集发送到调用 API。如果我不使用临时表来存储这些中间结果,我会怎么做?
没有看到查询很难说,但我会考虑将这些“确定”查询重写为包含所需值的单个选择,以便可以将选择作为游标返回。
我省略的那些查询是一堆选择语句和 IF ... THEN ... ELSE 取决于选择结果,以便设置一些内部变量。最后,确定变量 V_OUCOME。我的问题是在我将光标设置为“SELECT ... from TEMPTOUTCOME”并为 LVSQUERY 变量打开它之后,删除临时表的内容是否安全?
光标将包含这些行。但是,这似乎不是解决问题的正确方法。例如,看看这篇文章。 ***.com/questions/987013/…
【参考方案1】:
我还没有真正测试过,但是从一般 ORACLE 知识的角度来看,一旦您打开游标,您就不再处理存储的数据。相反,您正在迭代内存中的快照。所以我相信它应该有效。除非有大量数据并且 oracle 会尝试对结果进行分页(但不确定它是否真的发生)...
作为一个简单/安全的选项,您可以删除一天/小时/分钟前的记录(取决于利用率)。
另外作为一个建议,如果您将 sysdate 放入一个变量并在您的插入中使用它,则处理数据集可能会容易得多。因为您可能只是按 origindate 查询。 它也会使插入速度更快
还有一个值得一看的东西(也许是最好的)是Oracle 临时表。
【讨论】:
以上是关于Oracle 存储过程 - 创建游标后我可以清空临时表吗的主要内容,如果未能解决你的问题,请参考以下文章