2017-07-24 73 views
1

我查了很多类似的问题,但没有人适用于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”作为订单金额和收入。

回答

4

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

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

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

有两种可能的解决方案:

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

按照某些标准,这可能在将连接条件从过滤器中分离出来时“更纯”,但它可能会相当复杂(因此容易出错)。需要注意的一个问题是,您可能需要将生成的NULL记录与恰好具有一些NULL数据的右表的“真实”记录区分开来。

测试连接键的右表值为NULL应该是合理安全的。你可以测试右表的PK值为NULL(假设你在该表上有一个真正的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记录(这意味着NULL记录之前,这将有效地过滤STOCK_ACTIVITY记录呈现给加盟仍然可以无干扰产生)。 (“有效地”,因为如你知道DBMS将遵循什么步骤说话是愚蠢的;我们所能说的是,这具有与通过某些步骤获得的相同的效果...)

+0

谢谢;这已经完成了这项工作。虽然两种不同的选择产生了不同的结果。有趣的是,将过滤器连接到连接中产生比where子句少2行的内容。 – Turab

+0

这可能意味着'STOCK_ACTIVITY'中有2条记录,'TYPE'真*是*'NULL'。我想我应该指出,如果使用选项1,则需要将“真实记录与某些”NULL“值区分开”形成“生成的”NULL记录“。在大多数敏感的数据模型中,您可以通过检查右表PK值是否为NULL来做到这一点。或者肯定你可以测试连接键的右表值是否为NULL(因为an = join不会匹配这样的记录) –

-1

保持聚合操作与JOIN分离。这是最干净的。首先进行分组,然后加入附加信息。

+0

没有解决问题,并且取决于DBMS可能是性能不佳的明智建议 –

+0

您是对的,但500行对于性能的观点来说并不重要,而间隔信息可以帮助理解逻辑 - 更好的学习曲线比写解决方案。没有恶意。 –

+0

您的意见被记录下来,但这是一个问题和答案网站,而不是一个问题,并告诉你,我认为是错误的,你要问它的网站。 –

0

如果没有STOCK_ACTIVITY对于CUSTOMER,则会连接一条满是NULL的行。这也意味着WHERE陈述AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE')永远不可能适用于这些行。

相关问题