我对使用FULL TABLE SCAN的查询有疑问。
当这个查询在我们的UAT环境中运行时,它使用一个TABLE ACCESS BY INDEX ROWID,但是在prod中 它使用FULL TABLE SCAN。 UAT比PROD运行得好得多。
我们在prod和uat中有相同的表和索引结构。
我已经尝试过重建和重新创建索引,但使用了相同的解释计划。
表和索引静态也已更新。
您能帮助我使用INDEX访问而不是FUll表访问吗?
请参阅下面我们的产品和uat的解释计划。
EXPLAIN PLAN PROD ===================================================================== SQL> explain plan for SELECT ASV_ODC_BRANCH.CODE, ASV_ODC_BRANCH.DESCRIPTION, ASV_ODC_BRANCH.BRSTN, DEB.VIEW_DORMANT.ACCTNO AS DORMANT_ACCT, DEB.VIEW_DORMANT.SHORTNAME AS DORMANT_NAME, DEB.VIEW_DORMANT.OPID_ENTRY, DEB.CUSTOMER.CUSTOMERNO, DEB.CUSTOMER.TIME_STAMP_ENTRY FROM ASV_ODC_BRANCH, DEB.VIEW_DORMANT, DEB.CUSTOMER WHERE trim(ASV_ODC_BRANCH.CODE) = decode(SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 1) || SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 7, 1), ’29’, SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 4, 3), SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 3)) AND DEB.VIEW_DORMANT.ACCTNO = DEB.CUSTOMER.CUSTOMERNO AND (DEB.VIEW_DORMANT.ACCTNO = :Xacct) ORDER BY ASV_ODC_BRANCH.CODE, DORMANT_ACCT; Explained. PLAN_TABLE_OUTPUT | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| | 0 | SELECT STATEMENT | | 3 | 489 | 3601 (2)| | 1 | SORT ORDER BY | | 3 | 489 | 3601 (2)| | 2 | HASH JOIN | | 3 | 489 | 3600 (2)| | 3 | MERGE JOIN CARTESIAN | | 1 | 90 | 3595 (2)| | 4 | NESTED LOOPS | | 1 | 66 | 3592 (2)| | 5 | **TABLE ACCESS FULL** | ACCOUNT | 1 | 56 | 3590 (2)| | 6 | TABLE ACCESS BY INDEX ROWID| EXTENSION1 | 1 | 10 | 2 (0)| | 7 | INDEX UNIQUE SCAN | PKEXT10 | 1 | | 1 (0)| | 8 | BUFFER SORT | | 1 | 24 | 3593 (2)| | 9 | TABLE ACCESS BY INDEX ROWID| CUSTOMER | 1 | 24 | 3 (0)| | 10 | INDEX RANGE SCAN | UXCUST1 | 1 | | 2 (0)| | 11 | TABLE ACCESS FULL | ASV_ODC_BRANCH | 334 | 24382 | 5 (0)| **EXPLAIN PLAN UAT** ====================================================================================== SQL> explain plan for SELECT ASV_ODC_BRANCH.CODE, ASV_ODC_BRANCH.DESCRIPTION, ASV_ODC_BRANCH.BRSTN, DEB.VIEW_DORMANT.ACCTNO AS DORMANT_ACCT, DEB.VIEW_DORMANT.SHORTNAME AS DORMANT_NAME, DEB.VIEW_DORMANT.OPID_ENTRY, DEB.CUSTOMER.CUSTOMERNO, DEB.CUSTOMER.TIME_STAMP_ENTRY FROM ASV_ODC_BRANCH, DEB.VIEW_DORMANT, DEB.CUSTOMER WHERE trim(ASV_ODC_BRANCH.CODE) = decode(SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 1) || SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 7, 1), ’29’, SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 4, 3), SUBSTR(DEB.VIEW_DORMANT.ACCTNO, 3, 3)) AND DEB.VIEW_DORMANT.ACCTNO = DEB.CUSTOMER.CUSTOMERNO AND (DEB.VIEW_DORMANT.ACCTNO = :Xacct) ORDER BY ASV_ODC_BRANCH.CODE, DORMANT_ACCT; Explained. SQL> / PLAN_TABLE_OUTPUT | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| | 0 | SELECT STATEMENT | | 5 | 5930 | 19 (11)| | 1 | SORT ORDER BY | | 5 | 5930 | 19 (11)| | 2 | HASH JOIN | | 5 | 5930 | 18 (6)| | 3 | MERGE JOIN CARTESIAN | | 2 | 2220 | 12 (0)| | 4 | NESTED LOOPS | | 1 | 1085 | 9 (0)| | 5 | **TABLE ACCESS BY INDEX ROWID**| ACCOUNT | 1 | 57 | 7 (0)| | 6 | INDEX SKIP SCAN | UXACCT2 | 1 | | 6 (0)| | 7 | TABLE ACCESS BY INDEX ROWID| EXTENSION1 | 1 | 1028 | 2 (0)| | 8 | INDEX UNIQUE SCAN | PKEXT10 | 1 | | 1 (0)| | 9 | BUFFER SORT | | 1 | 25 | 10 (0)| | 10 | TABLE ACCESS BY INDEX ROWID| CUSTOMER | 1 | 25 | 3 (0)| | 11 | INDEX RANGE SCAN | UXCUST1 | 1 | | 2 (0)| | 12 | TABLE ACCESS FULL | ASV_ODC_BRANCH | 336 | 25536 | 5 (0)|
答案 0 :(得分:2)
区别在于
EXPLAIN PLAN PROD
| 5 | **TABLE ACCESS FULL** | ACCOUNT | 1 | 56 | 3590 (2)|
EXPLAIN PLAN UAT
| 5 | **TABLE ACCESS BY INDEX ROWID**| ACCOUNT | 1 | 57 | 7 (0)|
| 6 | INDEX SKIP SCAN | UXACCT2 | 1 | | 6 (0)|
它是如何运作的?
不是使用语句中的谓词限制搜索路径,而是通过探测前缀列的不同值的索引来启动Skip Scans。然后将这些不同值中的每一个用作常规索引搜索的起始点。结果是对单个索引进行多次单独搜索,这些索引在组合时消除了前缀列的影响。从本质上讲,索引已从第二级搜索下来。
优化程序使用统计信息来确定跳过扫描是否比全表扫描更有效。
优化器认为他比FTS更有优势,因为
您可以考虑以下
答案 1 :(得分:1)
从我所看到的情况来看,表DEB.VIEW_DORMANT
是关于表ACCOUNT
和EXTENSION1
的视图,您希望使用前者的索引UXACCT2
。我想这个请求中的a hint应该允许你做你想做的事情,比如:
SELECT /*+ INDEX(D UXACCT2) */ ASV_ODC_BRANCH.CODE,
...
FROM ASV_ODC_BRANCH, DEB.VIEW_DORMANT D, DEB.CUSTOMER
...
PS:如果这是你管理的查询(不是由任何高级软件生成的),我建议你像我一样使用别名,这使查询更具可读性......
答案 2 :(得分:0)
我可以帮你做一个INDEX-ACCESS而不是FTS吗?大概...
你真的想要吗? - 可能不是
为什么数据库的行为不同?因为您对不同的数据进行操作 - 如您的示例所示,查询返回不同的行数,因此我甚至不必询问您的生产和测试数据是否相同。如果您在索引列中有不同的数据(不同的数量或不同的值),那么Database-Stats将会有所不同,您的索引看起来会有所不同,因此优化程序将会使用不同的查询计划!
你应该怎么做?确保所有索引都是最新的,您的分区是安全的,所有数据库统计信息都是最新的,没有奇怪的调整设置(查询计划,环境设置...)然后优化器在大多数情况下会找到最好的计划 - 在许多情况下,全表扫描是更快的替代方案。
但是如果你测量时间并且优化器显然采用了错误的路径,尽管它具有准确的表统计信息,你应该使用oracle提交bug报告。
如果您有其他理由希望优化器进行索引访问:
如果您无法像Emmanuel那样输入Optimizer-HINT,您可以尝试使用Profiles或Baselines,它们提供了很好的调整功能。您可以使用不同的WHERE-Predicates编写自己的语句,直到获得具有index-access的计划并将其用作SQL-Profile并将此配置文件链接到原始语句,然后将使用相同的查询计划。