【问题标题】:Joining onto NULL if Record does not exist SQL如果 Record 不存在 SQL 则加入 NULL
【发布时间】:2023-03-23 20:00:01
【问题描述】:

我有一个查询,它从表 CustomerDetails 中选择客户,然后左连接到另一个表 (CustomerActivity) 以获取他们的最后登录时间,最后左连接到另一个表 (OpenOrderDetails) 以获得他们的最后一次登录时间未结订单(如适用)。我还有一个很大的 WHERE 子句过滤这些数据

一个客户在任何时候都只能在OpenOrderDetails 表中拥有一条记录。我的查询如下所示:

SELECT CD.*, H.LastCustomerLoginTime, OD.OrderFiledDate, OD.OrderCompletedDate
    FROM CustomerDetails CD
    LEFT JOIN CustomerActivity H ON H.CustomerID = CD.CustomerID 
    LEFT JOIN OpenOrderDetails OD ON CD.CustomerID = OD.CustomerID
WHERE CD.OrderStatus IN (1,2,3)
    AND (CustomerType = 1 or (CustomerType = 3 and CustomerActive IN (1,2)))
    AND (OD.OrderFiledDate IS NULL OR CD.TimeStamp >= OD.OrderFiledDate)
    AND (OD.OrderCompletedDate IS NULL OR CD.TimeStamp <= OD.OrderCompletedDate)

我的问题是这个查询只返回在OpenOrderDetails 表中有记录的客户记录。如何返回每个客户,如果存在 OrderFiledDate/OrderCompletedDate,如果 OpenOrderDetails 表中不存在该客户的记录,则返回 NULL?

【问题讨论】:

  • 来自哪些表:字段customerTypeCustomerActive?这是一个常见的操作顺序问题。您希望保留连接的表上的过滤器作为连接的一部分出现。如果它们出现在 where 子句中,则 LEFT 连接的行为类似于内部连接。从逻辑上讲,这些表是连接的。然后 where 子句会消除不符合条件的值;包括由左连接生成的 NULLS。对 OD 的 is null 检查是一种不太常见但可接受的方法来保留左连接中的值,这就是我询问 CustomerActive 和 CustomerType 的原因。
  • *这个问题可能比简单的 where 子句限制更多。我猜想customerTypeCustomerActive 中的一个或两个不在customerDetails 中。并且它也需要被移动到连接中或进行 is null 检查。
  • 什么是DLECS.CustomerID 左二联?不应该是OD.CustomerID吗?定义的查询中不存在别名/表 DLECS。
  • @xQbert,打错字了,应该是 CD。根据您的问题,我同意您的关注,但选择的答案讲述了一个不同的故事。这个问题对我来说看起来很可疑;)
  • 耸耸肩继续前进:P

标签: sql sql-server tsql left-join


【解决方案1】:

在执行 LEFT JOIN 时,在 WHERE 子句中引用任何右侧表列都会将其转换为 INNER JOIN。要解决这个问题,只需从 WHERE 子句中删除所有引用“右侧”列的谓词并将它们移至 LEFT JOIN 条件。

类似的东西...

SELECT
    CD.*, 
    H.LastCustomerLoginTime, 
    OD.OrderFiledDate, 
    OD.OrderCompletedDate
FROM
    CustomerDetails CD
    LEFT JOIN CustomerActivity H
        ON H.CustomerID = CD.CustomerID
    LEFT JOIN OpenOrderDetails OD
        ON CD.CustomerID = DLECS.CustomerID
        AND (   OD.OrderFiledDate IS NULL
            OR  CD.TimeStamp >= OD.OrderFiledDate
        )
        AND (   OD.OrderCompletedDate IS NULL
                OR  CD.TimeStamp <= OD.OrderCompletedDate
            )
WHERE
    CD.OrderStatus IN ( 1, 2, 3 )
    AND (
            CustomerType = 1
            OR  
            (   
                CustomerType = 3
                AND CustomerActive IN ( 1, 2 )
            )
        );

【讨论】:

  • 澄清:在进行 LEFT JOIN 时,引用 WHERE 子句中的任何右侧表列不允许 NULL 会将其转换为 INNER JOIN。
  • @HABO - 从技术上讲......是的,你是对的......但是没有必要将正在努力学习的人与奇怪的语法混淆。
【解决方案2】:

只需将一些OpenOrderDetails 条件移动到join 子句

 SELECT CD.*, H.LastCustomerLoginTime, OD.OrderFiledDate, OD.OrderCompletedDate
FROM CustomerDetails CD
LEFT JOIN CustomerActivity H ON H.CustomerID = CD.CustomerID 
LEFT JOIN OpenOrderDetails OD   ON CD.CustomerID = DLECS.CustomerID
                    AND (OD.OrderFiledDate IS NULL OR CD.TimeStamp >= OD.OrderFiledDate)
                    AND (OD.OrderCompletedDate IS NULL OR CD.TimeStamp <= OD.OrderCompletedDate)
WHERE CD.OrderStatus IN (1,2,3)
AND (CustomerType = 1 or (CustomerType = 3 and CustomerActive IN (1,2)))

【讨论】:

  • 虽然我同意这更容易阅读/遵循。我看不出它会如何改变结果。 OD 字段的 is null 检查和使用 OR 将保留左连接中的值。那么为什么这行得通而 user1501171 不行呢?
  • 得承认我很震惊这个工作和你原来的查询没有@user1501171。我看不出 DLECS 是从哪里来的……
猜你喜欢
  • 2018-09-17
  • 2012-06-07
  • 1970-01-01
  • 2019-07-07
  • 2011-10-28
  • 1970-01-01
  • 2021-02-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多