2

我有一个前 N 个查询给我带来了问题。

首先,我有如下查询:

select /*+ gather_plan_statistics */ * from 
(
  select rowid
  from payer_subscription ps
  where  ps.subscription_status = :i_subscription_status 
  and ps.merchant_id           = :merchant_id2
  order by transaction_date desc
) where rownum <= :i_rowcount; 

此查询运行良好。它可以非常有效地为我找到海量数据集的前 10 行,使用关于 Mercer_id、subscription_status、transaction_date 的索引。

-------------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |             |      1 |        |     10 |00:00:00.01 |       4 |
|*  1 |  COUNT STOPKEY                |             |      1 |        |     10 |00:00:00.01 |       4 |
|   2 |   VIEW                        |             |      1 |     11 |     10 |00:00:00.01 |       4 |
|*  3 |    INDEX RANGE SCAN DESCENDING| SODTEST2_IX |      1 |    100 |     10 |00:00:00.01 |       4 |
-------------------------------------------------------------------------------------------------------

如您所见,每个阶段的估计实际行数为 10,这是正确的。

现在,我需要获取一组商家 ID 的前 N ​​条记录,因此如果我将查询更改为包含两个商家 ID,则性能坦克:

select /*+ gather_plan_statistics */ * from 
(
  select  rowid
  from payer_subscription ps
  where  ps.subscription_status = :i_subscription_status 
      and (ps.merchant_id = :merchant_id or 
           ps.merchant_id = :merchant_id2 )
  order by transaction_date desc
) where rownum <= :i_rowcount; 

    ----------------------------------------------------------------------------------------------------------------------------
| Id  | Operation               | Name        | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |             |      1 |        |     10 |00:00:00.17 |     178 |       |       |          |
|*  1 |  COUNT STOPKEY          |             |      1 |        |     10 |00:00:00.17 |     178 |       |       |          |
|   2 |   VIEW                  |             |      1 |    200 |     10 |00:00:00.17 |     178 |       |       |          |
|*  3 |    SORT ORDER BY STOPKEY|             |      1 |    200 |     10 |00:00:00.17 |     178 |  2048 |  2048 | 2048  (0)|
|   4 |     INLIST ITERATOR     |             |      1 |        |  42385 |00:00:00.10 |     178 |       |       |          |
|*  5 |      INDEX RANGE SCAN   | SODTEST2_IX |      2 |    200 |  42385 |00:00:00.06 |     178 |       |       |          |
----------------------------------------------------------------------------------------------------------------------------

现在请注意,有 42K 行来自两次索引范围扫描——当索引范围扫描达到 10 行时,Oracle 不再中止索引范围扫描。我认为会发生的是,Oracle 最多会为每个 Mercer_id 获取 10 行,因为知道查询最多将返回 10 行。然后它会根据交易日期对 10 + 10 行进行排序并输出前 10 行,但它拒绝这样做。

当我需要将商家列表传递到查询中时,有谁知道如何获得第一个查询的性能?我可能可以使用 union all 获得性能,但商家列表是可变的,可能介于 1 或 2 到几百个之间。

4

3 回答 3

1

您可以使用--+ use_concat提示使 Oracle 像 UNION ALL 一样执行查询。

文档

USE_CONCAT 提示指示优化器使用 UNION ALL 集合运算符将查询的 WHERE 子句中的组合 OR 条件转换为复合查询。如果没有这个提示,只有当使用连接的查询成本比没有它们的成本低时才会发生这种转换。USE_CONCAT 提示覆盖了成本考虑。

于 2012-07-27T14:22:21.763 回答
1

在很多情况下 use_concat 会被忽略。请参阅:MOS 注意:不同版本的 USE_CONCAT 提示(文档 ID 259741.1)

我在 10.2.0.4、11.2.0.1 中使用 OR_EXPAND 取得了成功,其中 USE_CONCAT 不起作用。/*+ OR_EXPAND(别名列名) */

记录在这里: http ://www.hellodba.com/reader.php?ID=199&lang=EN

于 2013-11-12T06:09:53.550 回答
0

我不确定这是否有帮助,但你可以尝试用 IN 替换 OR 运算符:

and ps.merchant_id IN (:merchant_id, :merchant_id2)
于 2012-07-27T14:24:48.500 回答