【问题标题】:Improving SQL Exists scalability改进 SQL 存在可伸缩性
【发布时间】:2013-12-11 11:11:36
【问题描述】:

假设我们有两个表,TEST 和 TEST_CHILDS,方式如下:

creat TABLE TEST(id1 number PRIMARY KEY, word VARCHAR(50),numero number);
creat TABLE TEST_CHILD (id2 number references test(id), word2 VARCHAR(50));

CREATE INDEX TEST_IDX ON TEST_CHILD(word2);
CREATE INDEX TEST_JOIN_IDX ON TEST_CHILD(id);

insert into TEST SELECT ROWNUM,U1.USERNAME||U2.TABLE_NAME, LENGTH(U1.USERNAME) FROM ALL_USERS U1,ALL_TABLES U2;
INSERT INTO TEST_CHILD SELECT MOD(ROWNUM,15000)+1,U1.USER_ID||U2.TABLE_NAME FROM ALL_USERS U1,ALL_TABLES U2;

我们想从 TEST 表中查询满足子表中某些条件的行,所以我们去:

SELECT /*+FIRST_ROWS(10)*/* FROM TEST T WHERE EXISTS (SELECT NULL FROM TEST_CHILD TC WHERE word2 like 'string%' AND TC.id = T.id ) AND ROWNUM < 10;

我们总是只需要前 10 个结果,不再需要了。因此,无论表有 10 个匹配值还是 1,000,000,我们都希望获得相同的响应时间来读取 10 个结果;因为它可以从子表中获得 10 个不同的结果并获得父表上的值(或者至少这是我们想要的计划)。但是在检查实际执行计划时,我们看到:

-----------------------------------------------------------------------------------------------
| Id  | Operation                       | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |             |     1 |    54 |     5  (20)| 00:00:01 |
|*  1 |  COUNT STOPKEY                  |             |       |       |            |          |
|   2 |   NESTED LOOPS                  |             |       |       |            |          |
|   3 |    NESTED LOOPS                 |             |     1 |    54 |     5  (20)| 00:00:01 |
|   4 |     SORT UNIQUE                 |             |     1 |    23 |     3   (0)| 00:00:01 |
|   5 |      TABLE ACCESS BY INDEX ROWID| TEST_CHILD  |     1 |    23 |     3   (0)| 00:00:01 |
|*  6 |       INDEX RANGE SCAN          | TEST_IDX    |     1 |       |     2   (0)| 00:00:01 |
|*  7 |     INDEX UNIQUE SCAN           | SYS_C005145 |     1 |       |     0   (0)| 00:00:01 |
|   8 |    TABLE ACCESS BY INDEX ROWID  | TEST        |     1 |    31 |     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------

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

   1 - filter(ROWNUM<10)
   6 - access("WORD2" LIKE 'string%')
       filter("WORD2" LIKE 'string%')
   7 - access("TC"."ID"="T"."ID")

SORT UNIQUE 在 STOPKEY 下,afaik 的意思是它正在从子表中读取所有结果,使 distinct 最终只选择前 10 个,使查询不像我们希望的那样可扩展。

我的例子有什么错误吗?

是否可以改进此执行计划以使其更好地扩展?

【问题讨论】:

    标签: oracle exists


    【解决方案1】:

    SORT UNIQUE 将查找 TEST_CHILD 中匹配“string%”的所有记录并对其进行排序 - 它不会从子表中读取所有结果。你的逻辑需要这个。如果您只从 TEST_CHILD 中选择了与“string%”匹配的前 10 行,并且这 10 行都具有相同的 ID,那么您的 TEST 最终结果将只有 1 行。

    无论如何,只要 'string%' 匹配 TEST_CHILD 中相对较少的行数,您的性能就应该没问题。如果您的情况是,'string%' 经常与 TEST_CHILD 上的大量记录计数匹配,那么在给定当前表的情况下,您无法做太多事情来提高 SQL 的性能。在这种情况下,如果这是一个任务关键型 SQL,并且性能与您的年度奖金相关联,那么您可能可以使用 MATERIALIZED VIEW 来做一些花哨的步法,例如为 TEST_CHILD 中的高基数 WORD2 值预先计算 10 个 TEST 行。

    最后一个想法 - 一个“有风险”的解决方案,但如果您没有数千个 TEST_CHILD 行与同一 TEST 行匹配,则应该可以使用以下解决方案:

    SELECT  * 
    FROM    TEST 
    WHERE   ID1 IN 
                (SELECT ID2 
                 FROM   TEST_CHILD 
                 WHERE  word2 like 'string%' 
                        AND ROWNUM < 1000) 
             AND ROWNUM <10;
    

    当然,您可以向上或向下调整 1000,但如果它太低,您可能会发现少于 10 个不同的 ID 值,这将使您得到少于 10 行的最终结果。

    【讨论】:

    • 只是把它放在那里——再一次,用“与此相关的奖金”的思路,但比 1000-children risky 方法更优雅:你可以用 PL/SQL 解决这个问题,编写一个函数,查询 TEST_CHILD 以匹配输入“string%”参数,通过结果后台处理并将 ID 排队,直到找到 10 个唯一 ID,然后返回 10 个唯一 ID 的数组。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-23
    • 2012-07-22
    • 2021-09-01
    • 2010-11-07
    • 1970-01-01
    相关资源
    最近更新 更多