将子选择转换为连接

Convert subselect to a join

我好像明白了 Join 比 sub-select 更受欢迎。 我看不出如何将 3 个子 select 转换为连接。

我的子select只获取第一行

如果不冒犯我,我完全愿意不理会它 SQL。

这是我的查询,是的,确实是 table 和列名

select x1.*, x2.KTNR, x3.J6NQ
from 
    (select D0HONB as HONB, D0HHNB as HHNB, 
        (
            select DHHHNB 
            from ECDHREP 
            where DHAOEQ = D0ATEQ and DHJRCD = D0KNCD 
            order by DHEJDT desc 
            FETCH FIRST 1 ROW ONLY
        ) as STC_HHNB,
        (
            select FIQ9NB 
            from DCFIREP 
            where FIQ7NB = D0Q7NB 
              AND FIBAEQ = D0ATEQ 
              and FISQCD = D0KNCD 
              and FIGZSZ in ('POS', 'ACT', 'MAN', 'HLD') 
            order by FIYCNB desc 
            FETCH FIRST 1 ROW ONLY
        ) as BL_Q9NB,
        (
            select AAKPNR 
            from C1AACPP 
            where AACEEQ = D0ATEQ and AARCCE = D0KNCD and AARDCE = D0KOCD 
            order by AAHMDT desc, AANENO desc 
            FETCH FIRST 1 ROW ONLY 
        ) as NULL_KPNR  
        from ECD0REP
    ) as x1 
left outer join (
        select AAKPNR as null_kpnr, max(ABKTNR) as KTNR 
        from C1AACPP 
          left outer join C1ABCPP on AAKPNR = ABKPNR 
        group by AAKPNR
    ) as X2 on x1.NULL_KPNR = x2.null_KPNR 
left outer join (
        select ACKPNR as KPNR, count(*) as J6NQ 
        from C1ACCPP 
        WHERE ACJNDD = 'Y' 
        group by ACKPNR
    ) as X3 on x1.NULL_KPNR = x3.KPNR 

您得到了相关子 select 和嵌套 table 表达式 (NTE) 的组合。

就我个人而言,如果我必须维护它,我会称之为冒犯。 ;)

考虑常见的 table 表达式和连接...没有你的数据和表结构,我不能给你真正的陈述,但一般形式看起来像

with 
   STC_HHNB as (
     select DHHHNB, DHAOEQ, DHJRCD, DHEJDT
      from ECDHREP )
,  BL_Q9NB as ( <....>
               where FIGZSZ in ('POS', 'ACT', 'MAN', 'HLD'))
<...>
select <...>
from stc_hhb
     join blq9nb on <...>

支持 CTE 而不是 NTE 的两个重要原因...CTE 的结果可以重复使用此外,使用 CTE 的增量构建语句也很容易。

re-used,我的意思是你可以

with 
  cte1 as (<...>)
  , cte2 as (select <...> from cte1 join <...>)
  , cte3 as (select <...> from cte1 join <...>)
  , cte4 as (select <...> from cte2 join cte3 on <...>)
select * from cte4;

优化器可以选择为cte1构建一个临时结果集,并多次使用。从建筑的角度来看,您可以看到我是在前面的每个 cte 上建造的。

这是一篇好文章 https://www.mcpressonline.com/programming/sql/simplify-sql-qwithq-common-table-expressions

编辑

让我们深入了解您的第一个相关 sub-query。

select D0HONB as HONB, D0HHNB as HHNB, 
      (
          select DHHHNB 
          from ECDHREP 
          where DHAOEQ = D0ATEQ and DHJRCD = D0KNCD 
          order by DHEJDT desc 
          FETCH FIRST 1 ROW ONLY
        ) as STC_HHNB
from ECD0REP

你要求数据库做的是对于在 ECD0REP 中读取的每一行,出去从 ECDHREP 中获取一行。如果你不走运,数据库将不得不读取 ECDHREP 中的大量记录才能找到这一行。通常,考虑到相关的 sub-query 内部查询需要读取 every 行。因此,如果外部有 M 行,内部有 N 行...那么您正在查看正在读取的 MxN 行。

我以前见过这个,尤其是在 IBM i 上。因为这就是 RPG 开发者的做法

read ECD0REP;
dow not %eof(ECD0REP);
  //need to get DHHHNB from ECDHREP 
  chain (D0ATEQ, D0KNCD) ECDHREP;
  STC_HHNB = DHHHNB;
  read ECD0REP;
enddo;

但这不是 SQL 中的方法。 SQL 是(应该)基于集合的。

因此,您需要做的是考虑如何 select 将 ECDHREP 中的记录集与您想要的 ECD0REP 中的记录集相匹配。

with cte1 as (
  select DHHHNB, DHAOEQ, DHJRCD
  from ECDHREP
)
select D0HONB as HONB
     , D0HHNB as HHNB
     , DHHHBN as STC_HHNB
from ECD0REP join cte1 
      on DHAOEQ = D0ATEQ and DHJRCD = D0KNCD

现在也许这不太正确。也许 ECDHREP 中有多行具有相同的值(DHAOEQ、DHJRCD);因此,您需要在相关 sub-query 中使用 FETCH FIRST。很好,您可以专注于 CTE 并弄清楚需要做什么才能获得您想要的那 1 行。也许 MAX(DHHHNB)MIN(DHHHNB) 会起作用。如果不出意外,您可以使用 ROW_NUMBER() 只挑出一行...

with cte1 as (
  select DHHHNB, DHAOEQ, DHJRCD
         , row_number() over(partition by DHAOEQ, DHJRCD
                             order by DHAOEQ, DHJRCD)
            as rowNbr  
  from ECDHREP
), cte2 as (
   select DHHHNB, DHAOEQ, DHJRCD
     from cte1
    where rowNbr = 1
)
select D0HONB as HONB
     , D0HHNB as HHNB
     , DHHHBN as STC_HHNB
from ECD0REP join cte2
      on DHAOEQ = D0ATEQ and DHJRCD = D0KNCD

现在您正在处理记录集,将它们连接在一起以获得最终结果。

更糟糕的情况,数据库必须读取 M + N 条记录。

这不是关于性能,而是关于集合的思考。

当然,使用相关 sub-query 的简单语句,优化器可能能够 re-write 将其加入连接。

但是最好写出最好的代码,而不是寄希望于优化器能够纠正它。

我已经看到并重写了 100 个相关和常规的查询 sub-queries...事实上,我看到一个查询必须分成 2 个,因为有两个 sub-queries.数据库的每个语句限制为 256。

如果 FETCH FIRST 1 ROW ONLY 子句是必要的,我将不得不在此处与 Charles 意见不同。在这种情况下,您可能无法将那些 sub-selects 拉出到 CTE 中,因为该 CTE 中只有一行。我怀疑您可以将外部 sub-select 拉入 CTE,但您仍然需要 CTE 中的 sub-selects。由于似乎没有共享,我将其称为个人偏好。顺便说一句,出于同样的原因,我不认为将 sub-selects 拉入一个连接对你有用。

sub-select 和 CTE 有什么区别?

with mycte as (
  select field1, field2 
  from mytable
  where somecondition = true)
select * 
from mycte

对比

select * 
from (select field1, field2 
      from mytable
      where somecondition = true) a

这真的只是个人喜好,虽然根据具体要求,一个 CTE 可以在 SQL 语句中多次使用,但 sub-select 在其他情况下会更正确,例如您问题中的 FETCT FIRST 子句。

编辑
我们先看第一个sub-query。使用适当的索引:

(
    select DHHHNB 
    from ECDHREP 
    where DHAOEQ = D0ATEQ and DHJRCD = D0KNCD 
    order by DHEJDT desc 
    FETCH FIRST 1 ROW ONLY
) as STC_HHNB,

只需在输出集中每行读取一条记录。我不认为这是非常繁重的。这对于第三个相关 sub-query 也是一样的。

第一个相关的索引 sub-query 将是:

create index ECDHREP_X1 
  on ECDHREP (DHAOEQ, DHJRCD, DHEJDT);

第二个相关 sub-query 可能每行需要多次读取,只是因为 IN 谓词,但它远不需要完整的 table 扫描。