【问题标题】:sql left join criteria in join vs where clausejoin vs where子句中的sql左连接条件
【发布时间】:2015-05-02 21:14:48
【问题描述】:

我有一个现有的查询来选择一些付款

我想过滤掉针对在另一个名为 ClientAlert 的表中具有活动警报的客户的任何付款

所以我想我会做一个左连接并检查 ClientAlertId 是否为空。

select      * 
from        payments p
left join   client c on c.clientid = p.clientid
left join   ClientAlert ca on ca.CRMId = c.CRMId and ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate())
where       
            ca.clientalertid is null and            
            p.PaymentStatusId = 2 and 
            p.PaymentDate <= GetDate() and 
            p.PaymentCategoryId = 1

我认为这似乎可行

但我有两个问题:

  1. 是否存在通过添加此联接导致返回多笔付款而不是一次付款的情况?

  2. 当我在 where 子句而不是 join 中指定以下内容时,它没有给出相同的结果,我不明白为什么

    and ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 and (ca.ExpiryDate is null or ExpiryDate > GetDate())

我认为在 where 子句中使用该标准将等同于在连接中使用它

【问题讨论】:

  • I thought having that criteria in the where clause woiuld be equivelent to having it in the joinINNER JOIN 可能是这样,但OUTER JOIN 是绝对错误的
  • 请编辑您的问题并使用表格别名限定列名。对于外连接,列的来源有很大的不同。
  • @GordonLinoff - 完成

标签: sql sql-server


【解决方案1】:
  1. 理论上,如果他们可以有多个警报。但是,由于您排除了带有警报的付款,所以这应该不是问题。如果你包括他们,它可能是。如果这是一个问题,您应该使用“not in”子查询而不是左外连接,因为如果不是 1:1,这可能会导致重复记录。

  2. 如果在 where 子句中包含条件,则如果整行不符合条件,则将其排除在外。将它放在 join 子句中意味着不显示连接的记录,但显示“父”。

【讨论】:

  • 他们可以并且确实有多个警报 - 但我认为因为我正在过滤那些没有警报(通过 ca.ClientAlertId = null),这不会导致重复付款?
  • 好的,那么它不会导致重复,因为您将它们过滤掉。
  • 另一件事是,如果您使用“不进入”而不是这种方法,性能可能会显着提高。虽然它会起作用,但在我拥有的大型数据库中的快速测试中,使用“not in”要快 10 倍。 YMMV
【解决方案2】:
  1. 如果链接到多个客户记录,您可以获得每条付款记录的倍数。不过,基于 WHERE 子句,我看不出多个 ClientAlert 记录如何导致重复。
  2. LEFT JOIN 记录在不匹配时在其所有列中返回 NULL。将ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 添加到 WHERE 子句基本上会强制连接的行为类似于 INNER JOIN,因为它必须找到匹配的记录,但我猜它永远不会返回数据,因为 ClientAlertId 是不可为空的列。因此,基本上您创建了一个查询,其中需要一个 NULL 行(表示没有警报),但该行必须包含数据。

【讨论】:

  • 他们可以并且确实有多个警报 - 但我认为因为我正在过滤那些没有警报(通过 ca.ClientAlertId = null),这不会导致重复付款?
【解决方案3】:
select      * 
from        payments p
left join   client c on c.clientid = p.clientid
left join   ClientAlert ca on ca.CRMId = c.CRMId 
and ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 
and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate())
where       
            ca.CRMId is null and            
            p.PaymentStatusId = 2 and 
            p.PaymentDate <= GetDate() and 
            p.PaymentCategoryId = 1
  1. 如果 clientid 在表客户端中是唯一的,这个微小的变化将确保您永远不会得到任何重复

  2. 将条件从左连接移动到 where 意味着您将左连接条件移动到行的条件中。因此,如果不满足条件,则不会返回该行。 左连接条件将始终包括连接左侧的行

【讨论】:

    【解决方案4】:

    首先,除非有与客户无关的付款,否则第一个联接应该是内部联接。

    其次,一旦客户收到适当的警报,您就可以过滤掉该客户支付的所有付款,即使是在警报生效前数月或数年支付的款项。这就是你想要的吗?

    第三个也是最后一个(虽然这是一个很长的):外连接的格式如下:

    select   ...
    from     InnerTable i
    left [outer] join OuterTable o
        on  <join criteria>
    where   <filter criteria>;
    

    仅对于外部联接,重要的是所有涉及外部表的检查都应被视为联接条件。至少熊市会有

        on  o.JoinField = i.OtherJoinField
    

    或者在你的情况下

        on ca.CRMId = c.CRMId
    

    为了保持外部联接的预期输出,其他外部表字段的检查也应作为附加联接条件进入 ON 子句:

        on  ca.CRMId = c.CRMId
        and ca.ClientAlertSubjectId = 1
        and ca.IsActive = 1
        and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate())
    

    如您所见,将这些附加检查添加到 WHERE 子句将完全过滤掉外连接的所有好处,以提供与内连接相同的结果集。

    但是,您可以通过稍作改动让输出返回到外连接结果集:

    where   (ca.clientalertid is null
             or (ca.ClientAlertSubjectId = 1
                 and ca.IsActive = 1
                 and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate()))
        and p.PaymentStatusId = 2
        and ...
    

    但这并没有相当回到正常的外部连接结果集。在您的情况下,常规外部联接将显示付款和客户数据,其中警报数据为 NULL:

    1. 客户付款,完全没有提醒。
    2. 带有警报但不完全符合附加条件的客户付款。例如,如果 IsActive 包含 0。

    然后会有完全合格警报的客户支付的警报数据。

    将附加条件移至 WHERE 子句但采用上述格式将显示付款和客户数据,其中警报数据为 NULLonly 用于根本没有警报的付款,以及完全没有警报的警报数据合格的警报。警报不完全合格的付款和客户根本不会出现。

    记住这一点。有时这正是您想要的。

    但这似乎不是您目前想要的。如果您不想看到任何警报和部分警报但过滤掉完全限定的警报,则不能将其他条件放在 WHERE 子句中。您的原始查询是执行此操作的唯一方法。

    确实没有理由将附加条件放在 WHERE 子句中。它什么也得不到。即使对于不影响输出的内部连接,也不会从中获得性能优势。将内部联接的执行计划与 ON 子句和 WHERE 子句中的附加条件进行比较。它们是相同的。外连接的相同比较是不同的。实际上,在 WHERE 子句中添加条件后,执行计划与内连接的执行计划相同。

    因此,只需使用您拥有的查询,并对您获得所需的输出感到满意。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-15
      • 2016-11-30
      • 1970-01-01
      • 2010-11-04
      • 2012-05-01
      • 1970-01-01
      相关资源
      最近更新 更多