【问题标题】:Reduce context switch减少上下文切换
【发布时间】:2020-09-02 17:30:27
【问题描述】:

问题:

Oracle SQL - 我有 2 个选择查询(比如 SQ1 和 SQ2)。 SQ1 和 SQ2 都选择一个 ID 值,但具有不同的连接条件、差异表和 where 条件。

场景 1:如果 SQ1 返回 1 行,则使用 select 语句的结果对表进行更新。 如果 SQ1 返回 0 行或多于 1 行,则控制应转到方案 2。

场景 2:如果 SQ2 返回 1 行,则使用 SQ2 语句的结果对表进行更新。 如果 SQ2 返回 0 或 >1 个结果,则调用另一个包过程。

如何将最少的上下文切换到数据库?

我是如何编码的:

select count(*) into v_count from table; ---SQ1
case if v_count=1 ...then do this...
Else
  select count(*) into v_count2 from table; ---SQ2  
     --2nd case starts here
     case if 
         v_count2=1 ...then do this...
         else call package.procedure. 
     end case;
end case;

要求

对数据库的调用最少。减少上下文切换。

【问题讨论】:

    标签: sql oracle oracle-sqldeveloper query-optimization context-switch


    【解决方案1】:

    SQ2 与 SQ1 是相同的 select 语句,但有额外的 where 条件。

    你可以只做一个这样的陈述

    SELECT 
       count(*),
       count( case when ... /*additional where condition for SQ2 */ then 1 end )
    INTO v_count, v_count2
    FROM table
    

    然后执行一系列 IF-THEN-ELSIF 语句:

    IF v_count = 1 THEN 
       ..do this...
    ELSIF v_count2=1  THEN 
       ...do that...
    ELSE 
       call package.procedure. 
    END IF;
    

    这样可以节省 1 次上下文切换和 1 次表扫描。

    【讨论】:

    • 道歉。我已经更新了这个问题。 SQ1 和 SQ2 都选择一个 ID 值,但具有不同的连接条件、差异表和 where 条件。
    • 很好的答案,但是来自 PL/SQL->SQL 引擎的上下文切换真的那么昂贵吗?我的理解一直是关注 SQL->PL/SQL 开关,但是 PL/SQL->SQL 开关在 Oracle 中得到了很好的优化。我怀疑 OP 通过这种优化不必要地使他的代码复杂化。
    • @Matthew McPeak 您需要测试才能知道确切的值,但是带有上下文切换的查询甚至可能比简单的 SQL 慢 30~50 倍(50 倍 --> 5000%)。
    • @krokodilko 这些是 SQL->PL/SQL 上下文切换,并且需要限制这些是众所周知的。但我看到的指导是 PL/SQL->SQL(即从 PL/SQL 块调用 SQL 语句)上下文切换得到了更好的优化并且问题更少。
    【解决方案2】:

    好吧,没有其他信息,这样的事情应该可以工作:

    update t
        set col = coalesce( (<SQL1>), (<SQL2>)
        where (select count(*) from <sql1>) = 1 or
              (select count(*) from <sql2) = 1;
    

    也就是说,我可能会修复您的过滤语句,使它们返回 0 或 1 行,从不超过 1。

    【讨论】:

    • 谢谢。但是该表配置为具有多个结果(超过 1 条记录)。因此,这是一种预期的情况。
    • 我们如何在 Coalesce 中放置 SQL 语句?这对我不起作用
    • @Natty 。 . .您需要一组额外的括号,就像任何标量子查询一样。
    • --这是我做的测试代码 DECLARE n int; BEGIN n := coalesce ((select 1 from dual),2); dbms_output.put_line(n);结束;
    • @Natty 。 . .为此,您不需要 PL/SQL。这只是一条 SQL 语句。