更改非相关子查询的访问方法

rfu*_*sca 7 oracle subquery

甲骨文 11g R2

不幸的是,我们的应用程序具有每行安全“功能”。我们有一个看起来像这样的查询:

坏,慢:

SELECT someRow, someOtherRow
FROM bigTableA a
WHERE EXISTS (
  SELECT 0 FROM bigTableA_securitymapping b 
  WHERE b.PrimaryKeyTableA = a.PrimaryKeyTableA AND
       b.accesscode in (SELECT accesscode 
                              FROM accesscodeView 
                              WHERE user = :someUserID)
)
Run Code Online (Sandbox Code Playgroud)

上有一个唯一索引bigTableA_securitymapping(PrimaryKeyTableA,accesscode)

accesscodeView有可能返回多个accesscode给定用户,所以它必须是IN()=

问题是此查询忽略了唯一索引bigTableA_securitymapping并选择执行全表扫描。

如果我将 the 更改IN()为 an=那么它会UNIQUE SCAN在唯一索引上执行 abigTableA_securitymapping并且大约快 50 倍。

好,快但不可能:

SELECT someRow, someOtherRow
    FROM bigTableA a
    WHERE EXISTS (
      SELECT 0 FROM bigTableA_securitymapping b 
      WHERE b.PrimaryKeyTableA = a.PrimaryKeyTableA AND
           b.accesscode =(SELECT distinct accesscode 
                                  FROM accesscodeView 
                                  WHERE user = :someUserID)
    )
Run Code Online (Sandbox Code Playgroud)

但是,我不能这样做,因为accesscodeView可能返回多于一行。

(那里有一个distinct,因为accesscodeView它给出了需要=,将DISTINCT放在原始查询上没有区别。)

如果我对访问代码进行硬编码,它也会UNIQUE SCANbigTableA_securitymapping.

好,快,但需要大量的应用程序更改:

SELECT someRow, someOtherRow
      FROM bigTableA a
      WHERE EXISTS (
        SELECT 0 FROM bigTableA_securitymapping b 
        WHERE b.PrimaryKeyTableA = a.PrimaryKeyTableA AND
             b.accesscode in (1,2,3,4)
      )
Run Code Online (Sandbox Code Playgroud)

更改为内部连接也无济于事。它仍然进行全表扫描。

坏,慢:

SELECT someRow, someOtherRow
FROM bigTableA a
WHERE EXISTS (
  SELECT 0 FROM accesscode ac INNER JOIN 
                bigTableA_securitymapping b ON
                  ac.accesscode = b.accesscode         
  WHERE b.PrimaryKeyTableA = a.PrimaryKeyTableA 
        AND user = :someUserID
)
Run Code Online (Sandbox Code Playgroud)

那么为什么=IN()in之间存在差异。为什么不相关的子查询(accesscodeview子查询)会导致这样的计划差异?有什么办法可以重写它来做我想做的事吗?此处“好计划”成本与“坏计划”成本的差异是 87 和 37,000,并且在实际运行时需要大量时间才能获得相同的结果。

小智 -1

这就是我要做的,尽管我并没有完全看到这里的全貌,也许我错过了一些东西。如果它没有用,一个例子将有助于完善我的答案。

SELECT someRow, someOtherRow
    FROM bigTableA a
    WHERE EXISTS (
      SELECT 0 FROM bigTableA_securitymapping b 
      WHERE b.PrimaryKeyTableA = a.PrimaryKeyTableA 
           AND EXISTS (SELECT 1 FROM accesscodeView 
                          WHERE user = :someUserID
                          AND accesscode = b.accesscode)
    )
Run Code Online (Sandbox Code Playgroud)

它总是返回一行而不是扫描一秒(或更多,如果有更多)