即使没有匹配项,如何 select 加入行?

How to select joined rows even if there is no match?

我检查了很多类似的问题,但我猜 none 适用于 Firebird。

我有两个table;一个存储客户信息,第二个存储库存活动(也包括订单)。我想获取所有客户以及他们的订单数量。但是不管我怎么加入命令table;我最终只得到至少有一份订单的客户。这意味着在库存活动中没有匹配项的客户 table 将不会出现在结果集中。

这是我的查询 运行;

SELECT
  C.NAME, C.GROUPNAME, C.EMAIL,
  COALESCE(COUNT(DISTINCT S.ORDERNO), '0') AS TOTALORDERS,
  COALESCE(SUM(S.AMOUNT), '0') as TOTALREVENUE
FROM CUSTOMERS C
LEFT OUTER JOIN STOCK_ACTIVITY S ON C.ID = S.CUSTOMERID
WHERE C.GROUPNAME = 'B'
  AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE')
GROUP BY C.NAME, C.GROUPNAME, C.EMAIL

没有联接,我得到 570 行(客户),这是正确的结果集。当我加入订单 table 以获取这些客户的总订单金额时;我只得到 379 个结果;这是至少有一个订单的。这意味着没有订单的客户不会 return。正如您可能已经猜到的那样;我想让客户的订单金额和收入为零 activity 到 return“0”。

将聚合操作与 JOIN 分开。那是最干净的。先做分组再加入附加信息

问题是您的 WHERE 子句过滤了 "right hand" table 的值。

WHERE ...
  AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE')

当外部联接为左侧 table 的 "unmatched" 行生成记录时,它为右侧 table 的所有列提供 NULL 值。所以 S.TYPE 对于这些记录来说是 NULL

有两种可能的解决方案:

  1. 在您的 WHERE 逻辑中明确允许“NULL 记录”情况。

根据某些标准,这可能是 "more pure" 将连接条件与过滤器分开,但它可能会变得相当复杂(因此容易出错)。需要注意的一个问题是,您可能必须将生成的 NULL 记录与恰好有一些 NULL 数据的右侧 table 的 "real" 记录区分开来。

测试连接键 table 的正确值为 NULL 应该是相当安全的。您可以测试正确的 table 的 PK 值为 NULL(假设您在 table 上有一个真正的 PK)。

  1. 将谓词从 WHERE 子句移动到外部连接的 ON 子句。

这个很简单,看起来像

SELECT C.NAME, C.GROUPNAME, C.EMAIL,
       COALESCE(COUNT(DISTINCT S.ORDERNO), '0') AS TOTALORDERS,
       COALESCE(SUM(S.AMOUNT), '0') as TOTALREVENUE
  FROM                 CUSTOMERS C
       LEFT OUTER JOIN STOCK_ACTIVITY S
                    ON C.ID = S.CUSTOMERID
                   AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE')
 WHERE C.GROUPNAME = 'B'
 GROUP BY C.NAME, C.GROUPNAME, C.EMAIL

这会在尝试将它们与 CUSTOMERS 记录进行匹配之前有效地过滤呈现给连接的 STOCK_ACTIVITY 记录(这意味着 NULL 记录仍然可以不受干扰地生成)。 ("Effectively" 因为说你知道 DBMS 将遵循哪些步骤是愚蠢的;我们只能说这与你遵循某些步骤所获得的效果相同...)

如果 CUSTOMER 没有 STOCK_ACTIVITY,则会附上一整行 NULL。这也意味着 WHERE 语句 AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE') 永远不会对这些行成立。