SQL Server左右连接疑惑:查询结果行数不符问题排查
问题分析:左连接后接内连接导致数据丢失
核心原因:左连接被后续内连接“覆盖”
原始查询中,BelegPos与personenkonto是左连接,但紧接着用内连接关联konto表:
LEFT OUTER JOIN integris.personenkonto ON BelegPos.personenkonto_id = integris.personenkonto.personenkonto_id INNER JOIN integris.konto ON integris.personenkonto.personenkonto_id = integris.konto.personenkonto_id
当BelegPos没有匹配的personenkonto记录时(比如BelegPos.personenkonto_id为NULL或无对应行),personenkonto.personenkonto_id会是NULL。此时内连接konto会把这些行直接过滤掉——因为NULL无法和任何值匹配,这就相当于把原本的左连接变成了内连接,导致大量符合条件的Beleg/BelegPos行被丢弃,最终只返回40万行。
修改后查询的逻辑修正
修改后的查询调整了连接顺序:先把personenkonto和konto做内连接,再用右连接关联BelegPos:
integris.personenkonto INNER JOIN integris.konto ON integris.personenkonto.personenkonto_id = integris.konto.personenkonto_id RIGHT OUTER JOIN integris.fibu_belegpos AS BelegPos ON integris.personenkonto.personenkonto_id = BelegPos.personenkonto_id
右连接的逻辑是保留BelegPos的所有行,即使personenkonto+konto没有匹配记录,这样就不会过滤掉那些没有对应账户信息的BelegPos行,自然能返回预期的90万行数据。
SSMS图表显示一致的误区
SSMS的可视化查询设计器会对连接逻辑做简化展示,它只呈现表之间的关联关系,不会准确反映连接的执行顺序和优先级。两次查询的表关联关系本质上是一样的,但连接的执行顺序和类型组合不同,导致实际结果差异巨大——图表只是直观展示,不能替代对SQL语句本身的逻辑分析。
正确的原始查询修改方案
不需要调整连接顺序,只需把personenkonto到konto的内连接改成左连接,就能保留左连接的效果:
SELECT Beleg.fibu_beleg_id FROM integris.fibu_beleg AS Beleg INNER JOIN integris.periode AS Periode ON Beleg.periode_id = Periode.periode_id INNER JOIN integris.belegart ON Beleg.belegart_id = integris.belegart.belegart_id LEFT OUTER JOIN integris.fibu_belegpos AS BelegPos ON Beleg.fibu_beleg_id = BelegPos.fibu_beleg_id LEFT OUTER JOIN integris.sachkonto AS Sachkonto ON Sachkonto.sachkonto_id = BelegPos.sachkonto_id LEFT OUTER JOIN integris.steuerschl ON BelegPos.steuerschl_id = integris.steuerschl.steuerschl_id LEFT OUTER JOIN integris.buchungsschl ON BelegPos.buchungsschl_nr = integris.buchungsschl.nummer LEFT OUTER JOIN integris.personenkonto ON BelegPos.personenkonto_id = integris.personenkonto.personenkonto_id LEFT OUTER JOIN -- 将INNER改为LEFT,保留无匹配的行 integris.konto ON integris.personenkonto.personenkonto_id = integris.konto.personenkonto_id WHERE (YEAR(Periode.von_dat) >= YEAR(CURRENT_TIMESTAMP) - 2)
或者用子查询预先关联personenkonto和konto,再左连接到BelegPos,逻辑更清晰:
SELECT Beleg.fibu_beleg_id FROM integris.fibu_beleg AS Beleg INNER JOIN integris.periode AS Periode ON Beleg.periode_id = Periode.periode_id INNER JOIN integris.belegart ON Beleg.belegart_id = integris.belegart.belegart_id LEFT OUTER JOIN integris.fibu_belegpos AS BelegPos ON Beleg.fibu_beleg_id = BelegPos.fibu_beleg_id LEFT OUTER JOIN integris.sachkonto AS Sachkonto ON Sachkonto.sachkonto_id = BelegPos.sachkonto_id LEFT OUTER JOIN integris.steuerschl ON BelegPos.steuerschl_id = integris.steuerschl.steuerschl_id LEFT OUTER JOIN integris.buchungsschl ON BelegPos.buchungsschl_nr = integris.buchungsschl.nummer LEFT OUTER JOIN ( -- 预先关联账户表,只保留有对应关系的记录 SELECT pk.personenkonto_id, k.* FROM integris.personenkonto pk INNER JOIN integris.konto k ON pk.personenkonto_id = k.personenkonto_id ) AS PersonKonto ON BelegPos.personenkonto_id = PersonKonto.personenkonto_id WHERE (YEAR(Periode.von_dat) >= YEAR(CURRENT_TIMESTAMP) - 2)
内容的提问来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

