将子选择转换为连接
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 扫描。
我好像明白了 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 扫描。