SQL使用内部联接为非现有行获取聚合为0

时间:2017-04-23 11:03:48

标签: sql sql-server

我正在使用SQL Server查询这三个看起来像的表(有一些额外的列但不相关):

  • 客户 - > Id,名称
  • 地址 - > Id,Street,StreetNo,CustomerId
  • 销售 - > AddressId,Week,Total

我想得到每周的总销售额和客户数(同时显示地址详情)。我已经提出了这个问题

mysql> DESCRIBE `maillog`;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| Id    | varchar(200) | NO   | PRI | Test    |       |
| email | varchar(255) | YES  | MUL | NULL    |       |
+-------+--------------+------+-----+---------+-------+

即使我的SQL技能几乎没有,它看起来像是在做它的工作。但是现在我希望每当一个客户没有特定周的销售(周数只是整数)时就能显示0。我想知道我是否应该在Sales表中获得周数的不同值,然后循环它们(不确定如何)

任何帮助?

由于

3 个答案:

答案 0 :(得分:2)

使用CROSS JOIN为所有客户和周生成行。然后使用LEFT JOIN引入可用的数据:

SELECT c.Name, a.Street, a.StreetNo, w.Week,
       COALESCE(SUM(s.Total), 0) as Total
FROM Customers c CROSS JOIN
     (SELECT DISTINCT s.Week FROM sales s) w LEFT JOIN
     Addresses a
     ON c.CustomerId = a.CustomerId LEFT JOIN
     Sales s
     ON s.week = w.week AND s.AddressId = a.AddressId
GROUP BY c.Name, a.Street, a.StreetNo, w.Week;

使用表别名很好,但别名应该是表名的缩写。因此,a Addresses而非Customers

答案 1 :(得分:1)

请尝试以下方法......

SELECT Name,
       Street,
       StreetNo,
       Week,
       SUM( CASE
                WHEN Total IS NULL THEN
                    0
                ELSE
                    Total
            END ) AS Total
FROM Customers a
JOIN Addresses b ON a.Id = b.CustomerId
RIGHT JOIN Sales c ON b.Id = c.AddressId
GROUP BY a.Name,
         c.Week,
         b.Street,
         b.StreetNo;

我已经在三个地方修改了你的陈述。首先是我将您的加入更改为SalesRIGHT JOIN。这将与INNER JOIN一样加入,但它也会保留JOIN右侧侧的表中没有匹配记录的记录或左侧的记录组,将NULL值放在结果数据集的字段中,这些字段来自JOIN的左侧。 LEFT JOIN的工作方式相同,但 left 表格中的任何额外记录都会被保留。

我已从您幸存的INNER中移除了INNER JOIN这个词。如果JOIN前面没有连接类型,则会执行INNER JOINJOININNER JOIN都被认为是正确的,但主流协议似乎是将INNER保留在外,RDBMS允许将其遗漏(SQL-Server会这样做)。您使用的仍然完全取决于您 - 我已将其留在此处以用于说明目的。

第三个更改是我添加了一个CASE语句,用于测试Total字段是否包含NULL值,如果该客户没有销售,则会显示该值那个星期。如果是,则SUM()会返回NULL,因此CASE语句会返回0。如果Total不包含NULL值,则会执行该分组的所有SUM()Total

请注意,我假设Total除了NULL之外不会有任何RIGHT JOIN个值。如果这个假设不正确,请告诉我。

另请注意,我假设Week表格中的Customer不会丢失Sales,或者如果有True表单您不想列出它们。如果这个假设不正确,请再次告诉我。

如果您有任何问题或意见,请随时发表评论。

答案 2 :(得分:1)

您应该生成周数,而不是使用DISTINCT。这在性能和可靠性方面更好。然后在Sales表上使用LEFT JOIN而不是INNER JOIN

SELECT   a.Name
        ,b.Street
        ,b.StreetNo
        ,weeks.[Week]
        ,COALESCE(SUM(c.Total),0) as Total
FROM Customers a
    INNER JOIN Addresses b ON a.Id = b.CustomerId
    CROSS JOIN (
        -- Generate a sequence of 52 integers (13 x 4)
        SELECT ROW_NUMBER() OVER (ORDER BY a.x) AS [Week]
        FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) a(x)
        CROSS JOIN (SELECT x FROM (VALUES(1),(1),(1),(1)) b(x)) b
    ) weeks
    LEFT JOIN Sales c ON b.Id = c.AddressId AND c.[Week] = weeek.[Week]
GROUP BY a.Name
        ,b.Street
        ,b.StreetNo
        ,weeks.[Week]