【问题标题】:SQL Joins : in, out, shake it all aboutSQL Joins : in, out, 彻底改变
【发布时间】:2011-05-05 12:37:21
【问题描述】:

我正在执行以下 sql 以返回一个数据,其中表 1 和 2 中的 dob 和地址都匹配。

select table1.dob
, table1.address
, sum(case when person_status in ('A','B','C') then 1 else 0 end) as 'ABC_count'
, sum(case when person_status in ('D','E') then 1 else 0 end) as 'DE_Count'
, sum(case when person_status in ('F','G') then 1 else 0 end) as 'FG_Count' 
from table1
inner join table2
on (table1.dob = table2.dob and table1.address = table2.address)
where table1.dob > @myDate
group by table1.dob, table1.address
order by table1.dob, table1.address

但是,当 table2 中没有匹配项且只有该数据时,我现在想从 table1 返回数据,我认为只需将内连接更改为左外连接就可以执行我需要的操作,但事实并非如此。

谢谢!

【问题讨论】:

  • 仅供参考,根据您的 cmets 关于调整性能的信息,请检查您的索引!你肯定想要一个关于 dob 字段的索引,可能还有 person_status 和 address 。如果您在Dob, Address 上放置一个覆盖索引(按此顺序!),它也会加快您的分组/排序操作。

标签: sql-server join inner-join outer-join


【解决方案1】:

如果连接中没有匹配项,则第二个表中的字段为 NULL,因此您必须检查 table2 中的 NULL 值。假设表 2 中的 dob 不是 NULL,这应该可以解决您的问题:

select table1.dob
, table1.address
, sum(case when person_status in ('A','B','C') then 1 else 0 end) as 'ABC_count'
, sum(case when person_status in ('D','E') then 1 else 0 end) as 'DE_Count'
, sum(case when person_status in ('F','G') then 1 else 0 end) as 'FG_Count' 
from table1
left outer join table2
on (table1.dob = table2.dob and table1.address = table2.address)
where table1.dob > @myDate and table2.dob is null
group by table1.dob, table1.address
order by table1.dob, table1.address

【讨论】:

  • 谢谢!你认为这种方法会比用子查询重写并且不存在更快吗?
  • 我不确定性能问题...我用谷歌搜索了一下,没有确切的结论,它还取决于您的数据库结构和索引。看起来 NOT EXISTS 方式,查看执行计划,消耗较少的 CPU,但它似乎更加 I/O 密集,并且可能是要获取数百万条记录的问题。看这个链接和cmetsblog.sqlauthority.com/2008/04/22/…
  • @NimChimpsky - EXISTSNOT EXISTS 通常比 join 或其他运算符(如 IN)快,因为它们短路 - 第一次受到打击时,它们会继续下一个条目。
【解决方案2】:

在这种情况下不是连接,你应该使用NOT EXISTS函数。

【讨论】:

  • 谢谢!子查询通常较慢,因此添加空值(如@il_guru 建议的那样)会更快?
  • 子查询和连接都被服务器转换成一组相似的执行步骤。还要注意“过早的优化”!你应该只开始考虑优化查询,在你发现它很慢之后,当然不要在查询仍然产生错误的结果集时打扰。
  • 它现在不会产生错误的结果集。然而,它将在数以亿计的记录上运行。所以我需要优化它。
【解决方案3】:

在我看来,LEFT JOIN 更干净,如果 LEFT JOIN 和 NOT EXISTS 的性能没有太大差异,你应该使用它。 @JNK 说“EXISTS 和 NOT EXISTS 通常比 join 或 IN 之类的其他运算符快,因为它们会短路 - 第一次受到打击时,它们会继续下一个条目”,但我的理解是 NOT EXISTS 和 NOT IN 是通常很昂贵,因为 sql server 必须遍历查找表中的所有记录以确保该条目实际上不存在,所以我不知道短路是如何工作的

【讨论】:

    【解决方案4】:

    您也可以在这里使用 EXCEPT 关键字。

    select table1.dob
    , table1.address
    , sum(case when person_status in ('A','B','C') then 1 else 0 end) as 'ABC_count'
    , sum(case when person_status in ('D','E') then 1 else 0 end) as 'DE_Count'
    , sum(case when person_status in ('F','G') then 1 else 0 end) as 'FG_Count' 
    from table1
    where table1.dob > @myDate
    EXCEPT
    select table1.dob
    , table1.address
    , sum(case when person_status in ('A','B','C') then 1 else 0 end) as 'ABC_count'
    , sum(case when person_status in ('D','E') then 1 else 0 end) as 'DE_Count'
    , sum(case when person_status in ('F','G') then 1 else 0 end) as 'FG_Count' 
    from table1
    inner join table2
    on (table1.dob = table2.dob and table1.address = table2.address)
    where table1.dob > @myDate
    

    这将获得第一个查询中不在第二个查询中的所有记录。

    【讨论】:

      猜你喜欢
      • 2015-06-23
      • 2015-12-14
      • 1970-01-01
      • 2013-08-24
      • 1970-01-01
      • 1970-01-01
      • 2020-10-23
      • 2019-12-01
      • 1970-01-01
      相关资源
      最近更新 更多