Oracle 存储过程 - 我可以在创建游标后清空临时 table
Oracle Stored Procedure - Can I empty temp table after cursor is created
我有一个 Web 服务 API,它使用项目 ID 列表作为输入参数,数据 table 作为输出参数(以及与此问题无关的其他参数)。此 API 调用包内的 Oracle 存储过程以获取输出数据的内容 table。
存储过程遍历每个项目 ID 并为其确定结果。然后它使用临时 table 来存储每个项目 ID(项目 ID、结果、系统日期)的结果。最后,使用游标查询此临时文件 table 并获取结果。
我的问题是,随着时间的推移,此数据的内容 table 变得太大(数百万条记录)。我知道我可以有一个清理过程,但想知道它是否接受 table 创建游标后删除内容。
这是 Web 服务 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;
我还没有真正测试过,但是从一般的 ORACLE 知识的角度来看,一旦你 打开 一个游标,你就不再处理存储的数据。相反,您正在迭代内存中的快照。所以我相信它应该有效。除非有大量数据并且 oracle 尝试对结果进行分页(但不确定它是否真的发生了)...
作为 simple/safe 选项,您可以删除 day/hour/minute 旧的记录(取决于使用情况)。
另外作为一个建议,如果你将 sysdate 一次放入一个变量中并在你的插入中使用它,那么处理数据集可能会容易得多。因为您可能只是按 origindate 查询。
它还将使插入速度更快一些
还有一件事要看(甚至可能是最好的)是 Oracle 临时表。
我有一个 Web 服务 API,它使用项目 ID 列表作为输入参数,数据 table 作为输出参数(以及与此问题无关的其他参数)。此 API 调用包内的 Oracle 存储过程以获取输出数据的内容 table。
存储过程遍历每个项目 ID 并为其确定结果。然后它使用临时 table 来存储每个项目 ID(项目 ID、结果、系统日期)的结果。最后,使用游标查询此临时文件 table 并获取结果。
我的问题是,随着时间的推移,此数据的内容 table 变得太大(数百万条记录)。我知道我可以有一个清理过程,但想知道它是否接受 table 创建游标后删除内容。
这是 Web 服务 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;
我还没有真正测试过,但是从一般的 ORACLE 知识的角度来看,一旦你 打开 一个游标,你就不再处理存储的数据。相反,您正在迭代内存中的快照。所以我相信它应该有效。除非有大量数据并且 oracle 尝试对结果进行分页(但不确定它是否真的发生了)...
作为 simple/safe 选项,您可以删除 day/hour/minute 旧的记录(取决于使用情况)。
另外作为一个建议,如果你将 sysdate 一次放入一个变量中并在你的插入中使用它,那么处理数据集可能会容易得多。因为您可能只是按 origindate 查询。 它还将使插入速度更快一些
还有一件事要看(甚至可能是最好的)是 Oracle 临时表。