我有一个现有的查询来选择一些付款
我想过滤掉另一个名为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
这似乎有效我认为
但我有两个问题:
是否会出现通过添加此联接而导致多次付款而不是一次付款的情况?
当我在where子句而不是join中指定以下内容时,它没有给出相同的结果,我也不明白为什么
和ca.ClientAlertSubjectId = 1且ca.IsActive = 1且(ca.ExpiryDate为null或ExpiryDate&gt; GetDate())
我认为在where子句woiuld中的那个标准对于在连接中具有等效性
答案 0 :(得分:1)
理论上如果他们可以有多个警报。但是,由于您要使用提醒排除付款,因此这应该不是问题。如果你包括它们可能是。如果这是一个问题,你应该使用“not in”子查询而不是左外连接,因为如果它不是1:1会导致重复记录。
如果与条件不匹配,则where子句中的条件会排除整行。在join子句中使用它意味着未显示连接记录,但“父”是。
答案 1 :(得分:1)
LEFT JOIN
记录会在其所有列中返回NULL。将ca.ClientAlertSubjectId = 1 and ca.IsActive = 1
添加到WHERE子句基本上会强制该连接的行为类似于INNER JOIN,因为它必须找到匹配的记录,但我猜它永远不会返回数据,因为ClientAlertId是一个不可为空的列。所以基本上你创建了一个查询,你需要一个NULL行(表示没有警报),但该行必须包含数据。答案 2 :(得分:1)
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
这个微小的变化将确保您永远不会得到任何重复项,如果clientid在表格客户端中是唯一的
将条件从左连接移动到where意味着您将左连接条件移动到该行的条件中。因此,如果不满足条件,则不返回该行。 左连接条件将始终包含连接左侧的行
答案 3 :(得分:0)
首先,除非有与客户关联的付款,否则第一次加入应该是内部联接。
其次,一旦客户端有适当的警报,您就会过滤掉该客户所做的所有付款,即使是在警报生效前数月或数年付款的情况。这就是你想要的吗?
第三个和最后一个(尽管这是一个很长的一个):外部联接具有以下格式:
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的付款和客户端数据:
然后,具有完全合格警报的客户将获得这些付款的警报数据。
将附加条件移至WHERE子句,但采用上面显示的格式,将显示支付和客户数据,对于没有警报的付款,仅提供警报数据 的NULL,以及完全警报数据合格的警报。付款和没有完全合格警报的客户根本不会显示。
请记住。有时候这正是你想要的。
但这似乎不是你现在想要的。如果要查看没有警报和部分警报但过滤掉完全限定的警报,则不能将其他条件放在WHERE子句中。您的原始查询是唯一的方法。
确实没有理由在WHERE子句中放置其他条件。它什么都没得到你。即使对于不影响输出的内部连接,也不会从中获得性能优势。将内部联接的执行计划与ON子句和WHERE子句中的其他条件进行比较。它们完全相同。外连接的相同比较是不同的。事实上,使用WHERE子句中的添加条件,执行计划与内部联接的执行计划相同。
因此,只需使用您拥有的查询,并对您获得所需的输出感到满意。