强制SQL子查询首先运行

时间:2017-07-26 15:39:34

标签: sql database oracle sql-execution-plan sql-optimization

我有一个(简化的)查询,如下:

SELECT DISTINCT this_.person_id AS y0_
  FROM coded_entity this_
 WHERE this_.code     = ?
   AND this_.tenant_id  = ?
   AND this_.person_id IN (SELECT person_id
                             FROM person that
                            WHERE that.date_voided IS NULL
                              AND that.ssn            = ?
                              AND that.tenant_id      = ? ) 

我希望能够强制Oracle始终首先执行最里面的子查询,因为我知道它总是会更具选择性。代码只有几个值,而ssn有更多的值。每个表都有相同的行数。

但是,在实际操作中,有时Oracle CBO会决定先执行coded_entity查询,导致时间慢得多。

有没有办法强制执行此操作而不将查询拆分为单独的调用?

3 个答案:

答案 0 :(得分:1)

我的第一个念头(无论是否有钥匙)都是这样想的:

SELECT DISTINCT this_.person_id AS y0_
  FROM coded_entity this_
 WHERE this_.code     = ?
   AND this_.tenant_id  = ?
   AND EXISTS (SELECT null
                 FROM person THAT_
                WHERE THAT_.date_voided IS NULL
                  AND THAT_.ssn            = ?
                  AND THAT_.tenant_id      = this_.tenant_id
                  AND THAT_.person_id      = this_.person_id) 

但更好的方法(如果person_id是人的关键,并且每个人都有零个或多个编码实体)将是这样的:

SELECT this_.person_id AS y0_
  FROM person_ this_
 WHERE this_.ssn        = ?
   AND this_.tenant_id  = ?
   AND this_.date_voided is null
   AND EXISTS (SELECT null
                 FROM coded_entity THAT_
                WHERE THAT_.code = ?
                  AND THAT_.tenant_id      = this_.tenant_id
                  AND THAT_.person_id      = this_.person_id) 

答案 1 :(得分:0)

不需要子查询。只需INNER JOIN并过滤唯一记录。让优化器更好地理解它。

SELECT DISTINCT this_.person_id AS y0_
FROM coded_entity this_
INNER JOIN person that ON this_.person_id = that.person_id
    AND this_.tenant_id = that.tenant_id
    AND that.date_voided IS NULL
    AND that.ssn = ?
 WHERE this_.code     = ?
   AND this_.tenant_id  = ?

答案 2 :(得分:0)

您始终可以重写脚本以使条件首先作为子选择应用,然后在其上应用其他条件,如下所示:

SELECT DISTINCT this_.person_id AS y0_
FROM (
SELECT *
  FROM coded_entity this_
 WHERE this_.person_id IN (SELECT person_id
                             FROM person that
                            WHERE that.date_voided IS NULL
                              AND that.ssn            = ?
                              AND that.tenant_id      = ? ) 
) sub
where this_.code     = ?
   AND this_.tenant_id  = ?

或者你可以用连接格式重写它:

SELECT DISTINCT this_.person_id AS y0_
  FROM coded_entity this_
  JOIN person that
    ON that.person_id = this_person_id
    AND that.date_voided IS NULL
    AND that.ssn            = ?
    AND that.tenant_id      = ?
 WHERE this_.code     = ?
   AND this_.tenant_id  = ?

或者我认为肯定会有效的是只要在两个表上正确收集统计数据,然后CBO就会正确决定。