即使没有匹配项,如何 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
。
有两种可能的解决方案:
- 在您的
WHERE
逻辑中明确允许“NULL
记录”情况。
根据某些标准,这可能是 "more pure" 将连接条件与过滤器分开,但它可能会变得相当复杂(因此容易出错)。需要注意的一个问题是,您可能必须将生成的 NULL
记录与恰好有一些 NULL
数据的右侧 table 的 "real" 记录区分开来。
测试连接键 table 的正确值为 NULL
应该是相当安全的。您可以测试正确的 table 的 PK 值为 NULL
(假设您在 table 上有一个真正的 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
这会在尝试将它们与 CUSTOMERS
记录进行匹配之前有效地过滤呈现给连接的 STOCK_ACTIVITY
记录(这意味着 NULL
记录仍然可以不受干扰地生成)。 ("Effectively" 因为说你知道 DBMS 将遵循哪些步骤是愚蠢的;我们只能说这与你遵循某些步骤所获得的效果相同...)
如果 CUSTOMER
没有 STOCK_ACTIVITY
,则会附上一整行 NULL
。这也意味着 WHERE
语句 AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE')
永远不会对这些行成立。
我检查了很多类似的问题,但我猜 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
。
有两种可能的解决方案:
- 在您的
WHERE
逻辑中明确允许“NULL
记录”情况。
根据某些标准,这可能是 "more pure" 将连接条件与过滤器分开,但它可能会变得相当复杂(因此容易出错)。需要注意的一个问题是,您可能必须将生成的 NULL
记录与恰好有一些 NULL
数据的右侧 table 的 "real" 记录区分开来。
测试连接键 table 的正确值为 NULL
应该是相当安全的。您可以测试正确的 table 的 PK 值为 NULL
(假设您在 table 上有一个真正的 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
这会在尝试将它们与 CUSTOMERS
记录进行匹配之前有效地过滤呈现给连接的 STOCK_ACTIVITY
记录(这意味着 NULL
记录仍然可以不受干扰地生成)。 ("Effectively" 因为说你知道 DBMS 将遵循哪些步骤是愚蠢的;我们只能说这与你遵循某些步骤所获得的效果相同...)
如果 CUSTOMER
没有 STOCK_ACTIVITY
,则会附上一整行 NULL
。这也意味着 WHERE
语句 AND (S.TYPE = 'RECEIPT' OR S.TYPE = 'INVOICE')
永远不会对这些行成立。