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 存储过程 - 创建游标后我可以清空临时表吗的主要内容,如果未能解决你的问题,请参考以下文章

如何从 PLSQL Oracle 过程中删除数据或清空游标?

oracle数据库的游标和存储过程怎么写?

使用函数在oracle中存储游标

Oracle使用存储过程返回查询游标

oracle存储过程返回游标,取值报错

Oracle --- 存储过程函数包游标触发器