即使没有匹配,如何选择连接的行?

时间:2017-07-24 15:34:29

标签: sql join firebird

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

我有两张桌子;一个存储客户信息,第二个存储股票活动(也包括订单)。我想取得所有客户以及他们所做的订单数量。但无论我如何加入订单表;我最终只有至少有一个订单的客户。这意味着在股票活动表中没有匹配的客户将不会显示在结果集中。

这是我运行的查询;

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行(客户)并且它是正确的结果集。当我加入订单表以获取这些客户的总订单金额时;我只得到379个结果;哪些是至少有一个订单的。这意味着没有订单的客户将无法返回。你可能已经猜到了;我想让客户没有活动返回" 0"作为订单金额和收入。

3 个答案:

答案 0 :(得分:4)

问题是您的WHERE子句会过滤“右手”表的值。

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

当外部联接为左表中的“不匹配”行生成记录时,它会为右表中的所有列提供NULL值。因此,对于这些记录,S.TYPENULL

有两种可能的解决方案:

  1. 明确允许NULL逻辑中的“WHERE记录”案例。
  2. 根据某些标准,在将连接条件与过滤器分离时可能会“更纯粹”,但它可能会变得相当复杂(因此容易出错)。需要注意的一个问题是,您可能必须将生成的NULL记录与刚刚碰巧有一些NULL数据的正确表格的“真实”记录区分开来。

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

    1. 将谓词从WHERE子句移动到外连接的ON子句。
    2. 这很简单,看起来像

      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
      

      这有效地过滤了呈现给联接的STOCK_ACTIVITY记录,然后尝试将它们与CUSTOMERS记录进行匹配(这意味着NULL记录仍然可以在没有干扰的情况下生成)。 (“有效”,因为你知道DBMS会遵循哪些步骤是愚蠢的说话;我们所能说的就是这与你通过某些步骤获得的效果相同......)

答案 1 :(得分:0)

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

答案 2 :(得分:-1)

将聚合操作与JOIN分开。那是最干净的。首先进行分组,然后加入其他信息。