我想优化使用 IN 子句和 regex_str 函数的存储过程。我不确定我应该如何进一步优化它?
Posted
技术标签:
【中文标题】我想优化使用 IN 子句和 regex_str 函数的存储过程。我不确定我应该如何进一步优化它?【英文标题】:I want to optimize a stored procedure that uses IN clause and a regex_str function. I am not sure that how I should optimize it more? 【发布时间】:2012-09-25 07:36:04 【问题描述】:我得到的响应时间约为 200 毫秒。 我想进一步优化它。 我怎样才能做到这一点?
CREATE OR REPLACE
PROCEDURE GETSTORES
(
LISTOFOFFERIDS IN VARCHAR2,
REF_OFFERS OUT TYPES.OFFER_RECORD_CURSOR
)
AS
BEGIN
OPEN REF_OFFERS FOR
SELECT
/*+ PARALLEL(STORES 5) PARALLEL(MERCHANTOFFERS 5)*/
MOFF.OFFERID,
s.STOREID,
S.LAT,
s.LNG
FROM
MERCHANTOFFERS MOFF
INNER JOIN STORES s ON MOFF.STOREID =S.STOREID
WHERE
MOFF.OFFERID IN
(
SELECT
REGEXP_SUBSTR(LISTOFOFFERIDS,'[^,]+', 1, LEVEL)
FROM
DUAL CONNECT BY REGEXP_SUBSTR(LISTOFOFFERIDS, '[^,]+', 1, LEVEL) IS NOT NULL
)
;
END
GETSTORES;
我正在使用 regex_substr 从 LISTOFOFFERIDS 中的逗号分隔字符串中获取 OfferID 列表。 我已经在 Stores 表的 STOREID 上创建了索引,但无济于事。 如果速度更快,实现相同目标的新方法也很好。
相同的类型声明:
create or replace
PACKAGE TYPES
AS
TYPE OFFER_RECORD
IS
RECORD(
OFFER_ID MERCHANTOFFERS.OFFERID%TYPE,
STORE_ID STORES.STOREID%TYPE,
LAT STORES.LAT%TYPE,
LNG STORES.LNG%TYPE
);
TYPE OFFER_RECORD_CURSOR
IS
REF
CURSOR
RETURN OFFER_RECORD;
END
TYPES;
选择计划揭示了以下信息:
Plan hash value: 1501040938
-------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 276 | 67620 | 17 (12)| 00:00:01 |
|* 1 | HASH JOIN | | 276 | 67620 | 17 (12)| 00:00:01 |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 276 | 61272 | 3 (34)| 00:00:01 |
| 4 | VIEW | VW_NSO_1 | 1 | 202 | 3 (34)| 00:00:01 |
| 5 | HASH UNIQUE | | 1 | | 3 (34)| 00:00:01 |
|* 6 | CONNECT BY WITHOUT FILTERING (UNIQUE)| | | | | |
| 7 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | OFFERID_INDEX | 276 | | 0 (0)| 00:00:01 |
| 9 | TABLE ACCESS BY INDEX ROWID | MERCHANTOFFERS | 276 | 5520 | 0 (0)| 00:00:01 |
| 10 | TABLE ACCESS FULL | STORES | 9947 | 223K| 13 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("MERCHANTOFFERS"."STOREID"="STORES"."STOREID")
6 - filter( REGEXP_SUBSTR ('M1-Off2,M2-Off5,M2-Off9,M5-Off4,M10-Off1,M1-Off3,M2-Off4,M3-Off2,M4-Of
f6,M5-Off1,M6-Off1,M8-Off1,M7-Off3,M1-Off1,M2-Off1,M3-Off1,M3-Off4,M3-Off5,M3-Off6,M4-Off1,M4-Off7,M2
-Off2,M3-Off3,M5-Off2,M7-Off1,M7-Off2,M1-Off7,M2-Off3,M3-Off7,M5-Off5,M4-Off2,M4-Off3,M4-Off5,M8-Off2
,M6-Off2,M1-Off5,M1-Off6,M1-Off9,M1-Off8,M2-Off6,M2-Off7,M4-Off4,M9-Off1,M6-Off4,M1-Off4,M1-Off10,M2-
Off8,M3-Off8,M6-Off3,M5-Off3','[^,]+',1,LEVEL) IS NOT NULL)
8 - access("MERCHANTOFFERS"."OFFERID"="$kkqu_col_1")
【问题讨论】:
能发一下select的解释方案吗? 选择方案如?你想要类型声明吗?我已经编辑了帖子以包含它。 Oracle 解释计划。 http://docs.oracle.com/cd/B10500_01/server.920/a96533/ex_plan.htm 我已经编辑了帖子并包含了计划。谢谢:) 好的。看来您需要商店索引(STOREID) 【参考方案1】:-
如果您的服务器支持它(似乎您想要它),请将提示更改为
/*+ PARALLEL(S 8) PARALLEL(MOFF 8)*/
。当您有别名时,您必须在提示中使用别名。
你应该试试APC(STORES(STOREID, LAT, LNG)
)推荐的复合索引
请回答问题:对于给出的示例,您获得了多少不同的商店 (select count(distinct storeid) from (your_query)
) 以及 STORES 表中有多少家商店? (Select count(*) from Stores
)?
你分析过dbms_stats.gather_table_stats
的表格吗?
我相信connect by
查询不是问题所在。运行时间为 0.02 秒。
【讨论】:
哦,我当时没有得到这个问题。 distinct查询返回9946,总数也是9946。查询返回13970条记录,是offer和store的join。报价总数为 13971。关于复合索引,我在所有这些字段上都有单独的索引,我应该将它们组合起来吗?分析后如何查看收集到的统计数据? 9946?哎哟。什么返回select count(*) from MERCHANTOFFERS
?我认为您所能做的就是放置并行提示(/*+ PARALLEL(S 8) */
)。
似乎复合索引不会太有用。您需要阅读整个表格STORES
。表 Stores 包含长列作为描述等?
没有商店只有三个字段 ID 和 Lat 和 Lng
从 MERCHANTOFFERS 返回 select count(*) 是什么?我认为您所能做的就是放置并行提示( /*+ PARALLEL(S 8) */)。【参考方案2】:
如果你看一下你解释的计划,每个步骤的时间安排是相同的:没有明显的候选人可以专注于调整。
您发布的示例有 50 个 OFFERID 令牌。那是代表吗?他们映射到 276 家商店——这是一个代表性的比例吗?是否有任何优惠适用于多个商店?
276 行约占行数的 2.7%,这是一个很小的 sliver:但是,由于 STORES 似乎是一个非常紧凑的表,因此索引读取是否比全表扫描快是微不足道的。
要从数据库中榨取更多汁液,唯一明显的做法就是在 STORES(STOREID, LAT, LNG) 上构建复合索引;大概它不是一个看到很多 DML 的表,因此额外索引的开销不会太多。
最后一点:您的查询在 0.2 秒内执行。那么你希望它快多少呢?
【讨论】:
您好,感谢您的回复!我想把它带到0.1ms左右。您可以建议任何其他方法吗?就像使用函数而不是 Stored proc .. 或任何东西一样......这里有点绝望:( 函数是一个返回值的存储过程。它不会有不同的性能配置文件。 “这里有点绝望” 但还不足以回答我关于数据量或偏差的问题。另外,你试过我建议的复合索引吗? 抱歉当时正在开会。是的,优惠可以打到不止一家商店。 50 个令牌不代表它只是一个随机数。在运行时,我们可以提供从 1 到 1000 的报价。还有其他方法可以更优化地实现 IN 查询吗?【参考方案3】:考虑在连接中删除正则表达式,这样连接可以快速发生。 如果连接列上有索引,则连接可能会从嵌套循环中移动 到某种散列连接。
一旦你有了那个结果集(希望行更少),然后用你的正则表达式过滤它。
您可能会发现 WITH 语句在这个场景中很有帮助。
按照这个顺序的东西。 (未经测试的例子)
WITH
base AS
(
SELECT /*+ PARALLEL(STORES 5) PARALLEL(MERCHANTOFFERS 5) */
moff.OFFERID,
s.STOREID,
s.LAT,
s.LNG
FROM MERCHANTOFFERS moff
INNER JOIN STORES s
ON MOFF.STOREID = S.STOREID
),
offers AS
(
SELECT REGEXP_SUBSTR(LISTOFOFFERIDS,'[^,]+', 1, LEVEL) offerid
FROM DUAL
CONNECT BY REGEXP_SUBSTR(LISTOFOFFERIDS, '[^,]+', 1, LEVEL) IS NOT NULL
)
SELECT base.*
FROM base,
offers
WHERE base.offerid = offers.offerid
Oracle 可以将这两个视图执行到内存表中,然后连接。
没有担保人。您的里程可能会有所不同。你在寻找想法。这是一个想法。 祝你好运。
如果我没记错提示章节,当您为表名设置别名时,您需要在提示中使用该别名。 /*+ PARALLEL(s 5) PARALLEL(moff 5) */
我很好奇您为什么决定将值 5 作为提示。我的印象是 Oracle 会根据系统负载和其他神秘情况为其选择最佳价值。
【讨论】:
临时表写入磁盘。因此,这种方法极不可能将性能提高 0.1 秒。 取决于表的大小。 嗨 EvilTech .. 你能告诉我以后如何应用正则表达式吗?正则表达式实际上将逗号分隔列表分隔为 IN 子句中可接受的列表......还有其他更好的方法吗? 我相信connect by
查询不是问题所在。它在 0.02 秒内运行。
嗯 .. 但是 IN 子句的任何替代方案?以上是关于我想优化使用 IN 子句和 regex_str 函数的存储过程。我不确定我应该如何进一步优化它?的主要内容,如果未能解决你的问题,请参考以下文章