想要从选择查询中处理 5000 条记录在 oracle 数据库中需要很长时间

Posted

技术标签:

【中文标题】想要从选择查询中处理 5000 条记录在 oracle 数据库中需要很长时间【英文标题】:Want to process 5000 records from the select query is taking long time in oracle database 【发布时间】:2013-11-19 15:47:56 【问题描述】:

每次我想处理 5000 条记录,如下所示。

我第一次想处理 1 到 5000 行的记录。 第二次我想处理从 5001 到 10000 行的记录。 第三次我想明智地处理从 10001 到 15001 行的记录

我不想使用过程或 PL/SQL。我将更改代码中的 rnum 值以获取 5000 条记录。

给定的查询需要 3 分钟才能从 3 个连接的表中获取记录。如何减少获取记录的时间。

select * from  (
SELECT to_number(AA.MARK_ID) as MARK_ID, AA.SUPP_ID as supplier_id, CC.supp_nm as SUPPLIER_NAME, CC.supp_typ as supplier_type, 
CC.supp_lock_typ as supplier_lock_type, ROW_NUMBER() OVER (ORDER BY AA.MARK_ID) as rnum 
from TABLE_A AA, TABLE_B BB, TABLE_C CC  
WHERE 
AA.MARK_ID=BB.MARK_ID AND 
AA.SUPP_ID=CC.location_id  AND 
AA.char_id='160' AND  
BB.VALUE_KEY=AA.VALUE_KEY AND 
BB.VALUE_KEY=CC.VALUE_KEY
AND AA.VPR_ID IS NOT NULL) 
where rnum >=10001  and rnum<=15000;

我尝试过以下情况,但没有运气。

我已经尝试过 /*+ USE_NL(AA BB) */ 提示。 我使用存在于where条件。但获取记录同样需要 3 分钟。

下表是详细信息。

select count(*) from TABLE_B;
-----------------
2275

select count(*) from TABLE_A;
-----------------
2405276

select count(*) from TABLE_C;
-----------------
1269767

我的内部查询总记录的结果是

SELECT count(*) 
from TABLE_A AA, TABLE_B BB, TABLE_C CC  
WHERE 
AA.MARK_ID=BB.MARK_ID AND 
AA.SUPP_ID=CC.location_id  AND 
AA.char_id='160' AND  
BB.VALUE_KEY=AA.VALUE_KEY AND 
BB.VALUE_KEY=CC.VALUE_KEY
AND AA.VPR_ID IS NOT NULL;
-----------------
2027055

where条件中所有使用的列都被正确索引了。

给定查询的解释表是...

计划哈希值:3726328503

-------------------------------------------------------------------------------------------------------
| Id  | Operation                  | Name             | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |                  |  2082K|   182M|       | 85175   (1)| 00:17:03 |
|*  1 |  VIEW                      |                  |  2082K|   182M|       | 85175   (1)| 00:17:03 |
|*  2 |   WINDOW SORT PUSHED RANK  |                  |  2082K|   166M|   200M| 85175   (1)| 00:17:03 |
|*  3 |    HASH JOIN               |                  |  2082K|   166M|       | 44550   (1)| 00:08:55 |
|   4 |     TABLE ACCESS FULL      | TABLE_C          |  1640 | 49200 |       |    22   (0)| 00:00:01 |
|*  5 |     HASH JOIN              |                  |  2082K|   107M|    27M| 44516   (1)| 00:08:55 |
|*  6 |      VIEW                  | index$_join$_005 |  1274K|    13M|       |  9790   (1)| 00:01:58 |
|*  7 |       HASH JOIN            |                  |       |       |       |            |          |
|   8 |        INLIST ITERATOR     |                  |       |       |       |            |          |
|*  9 |         INDEX RANGE SCAN   | TABLE_B_IN2      |  1274K|    13M|       |  2371   (2)| 00:00:29 |
|  10 |        INDEX FAST FULL SCAN| TABLE_B_IU1      |  1274K|    13M|       |  4801   (1)| 00:00:58 |
|* 11 |      TABLE ACCESS FULL     | TABLE_A          |  2356K|    96M|       | 27174   (1)| 00:05:27 |
-------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("RNUM">=10001 AND "RNUM"<=15000)
   2 - filter(ROW_NUMBER() OVER ( ORDER BY "A"."MARK_ID")<=15000)
   3 - access("A"."SUPP_ID"="C"."LOC_ID" AND "A"."VALUE_KEY"="C"."VALUE_KEY")
   5 - access("A"."MARK_ID"="A"."MARK_ID" AND "A"."VALUE_KEY"="A"."VALUE_KEY")
   6 - filter("A"."MARK_CHN_IND"='C' OR "A"."MARK_CHN_IND"='D')
   7 - access(ROWID=ROWID)
   9 - access("A"."MARK_CHN_IND"='C' OR "A"."MARK_CHN_IND"='D')
  11 - filter("A"."CHNL_ID"=160 AND "A"."VPR_ID" IS NOT NULL)

您能否请任何人帮助我调整此查询,因为我正在尝试从过去 2 天开始?

【问题讨论】:

为您的索引命名,因为并非所有索引都已使用,因此使用 FTS。尝试并行提示?试图强制指数低谷提示? (考虑到根据 where 子句对表进行索引) 您想要索引名称吗?我对 oracle 提示一无所知。对于并行使用,我们是否需要更改表中的任何内容 什么都没有,只使用select /*+ parallel (table,N) cols from table; 如果这不起作用去alter session 以启用并行 很抱歉在这里再次问你,我有 3 个表,我必须在并行提示中使用哪个表。表数较高或表数较低,否则需要提及所有表,例如 /*+ parallel (table A, table_b,table_c,16) ?你能建议我这个吗.. 在痛点上使用它。根据您的计划,我建议在表 a 和表 c 上使用它 【参考方案1】:

每个查询都需要很长时间,因为每个查询都必须连接然后对所有行进行排序。 row_number 分析函数只有在读取了整个集合后才能返回结果。这是非常低效的。如果数据集很大,你只需要排序和哈希连接一次。

您应该一次获取整个集合,使用 5k 行的批次。或者,如果您想保留现有的代码逻辑,可以将结果存储在临时表中,例如:

CREATE TABLE TMP AS <your above query>
CREATE INDEX ON TMP (rnum)

然后将代码中的查询替换为

SELECT * FROM TMP WHERE rnum BETWEEN :x AND :y

显然,如果您的临时表被定期重用,只需创建一次并在完成后删除(或使用真正的temporary table)。

【讨论】:

非常感谢文森特!【参考方案2】:

您在 TABLE_A 中有多少个唯一的 MARK_ID 值?我认为如果您通过 MARK_ID 而不是人工行号来限制获取的记录范围,您可能会获得更好的性能,因为后者显然是不可分割的。诚然,您可能无法在每个范围内准确获得 5000 行,但我觉得它不如查询性能重要。

【讨论】:

【参考方案3】:

首先,给出混淆的表名使得几乎不可能推断出有关表之间的数据分布和关系的任何信息,因此潜在的回答者从一开始就被削弱了。

但是,如果 table_a 中的每一行都与其他表中的一行匹配,那么您可以通过将排名向下推到内联视图或公用表表达式中来避免使用 200Mb 的临时磁盘空间,这可能会削弱性能。

监控 V$SQL_WORKAREA 以检查用于窗口函数的确切空间量,如果仍然过多,请考虑修改内存管理以增加可用排序区域大小。

类似:

with cte_table_a as (
  SELECT
    to_number(MARK_ID) as MARK_ID,
    SUPP_ID as supplier_id,
    ROW_NUMBER() OVER (ORDER BY MARK_ID) as rnum 
  from
    TABLE_A
  where
    char_id='160' and
    VPR_ID IS NOT NULL)
select ...
from
  cte_table_a aa,
  TABLE_B BB,
  TABLE_C CC  
WHERE 
  aa.rnum      >= 10001          and
  aa.rnum      <= 15000          and
  AA.MARK_ID   =  BB.MARK_ID     AND 
  AA.SUPP_ID   =  CC.location_id AND 
  BB.VALUE_KEY =  AA.VALUE_KEY   AND 
  BB.VALUE_KEY =  CC.VALUE_KEY

【讨论】:

以上是关于想要从选择查询中处理 5000 条记录在 oracle 数据库中需要很长时间的主要内容,如果未能解决你的问题,请参考以下文章

在 python 中高效处理约 5000 万条记录文件

如何提高 SQLite 数据库的性能?

SQL性能优化(Oracle)

oracle提高查询效率的34条方法

在程序开发中怎样写SQL语句可以提高数据库的性能

SQL:如何从重复行中选择第一条记录?