我检查了很多类似的问题,但我猜不会适用于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"作为订单金额和收入。
答案 0 :(得分:4)
问题是您的WHERE
子句会过滤“右手”表的值。
WHERE ...
AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE')
当外部联接为左表中的“不匹配”行生成记录时,它会为右表中的所有列提供NULL
值。因此,对于这些记录,S.TYPE
为NULL
。
有两种可能的解决方案:
NULL
逻辑中的“WHERE
记录”案例。 根据某些标准,在将连接条件与过滤器分离时可能会“更纯粹”,但它可能会变得相当复杂(因此容易出错)。需要注意的一个问题是,您可能必须将生成的NULL
记录与刚刚碰巧有一些NULL
数据的正确表格的“真实”记录区分开来。
将连接键的正确表的值测试为NULL
应该是相当安全的。您可以测试正确的表的PK值为NULL
(假设您在该表上有一个真正的PK)。
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
这有效地过滤了呈现给联接的STOCK_ACTIVITY
记录,然后尝试将它们与CUSTOMERS
记录进行匹配(这意味着NULL
记录仍然可以在没有干扰的情况下生成)。 (“有效”,因为你知道DBMS会遵循哪些步骤是愚蠢的说话;我们所能说的就是这与你通过某些步骤获得的效果相同......)
答案 1 :(得分:0)
如果STOCK_ACTIVITY
没有CUSTOMER
,则会附加一行NULL
个。{这也意味着WHERE
语句AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE')
对于这些行永远不会成立。
答案 2 :(得分:-1)
将聚合操作与JOIN分开。那是最干净的。首先进行分组,然后加入其他信息。