使用内部联接将不存在行的SQL Get聚合为0

使用内部联接将不存在行的SQL Get聚合为0,sql,sql-server,Sql,Sql Server,我正在使用SQL Server查询这三个表,它们看起来像(有一些额外的列,但没有那么相关): 客户->Id,名称 地址->Id、街道、街道编号、客户Id 销售->地址ID,周,总计 我想得到每周的总销售额和客户(同时显示地址详细信息)。我提出了这个问题 SELECT a.Name, b.Street, b.StreetNo, c.Week, SUM (c.Total) as Total FROM Customers a INNER JOIN Addresses b ON a.Id =

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

  • 客户->Id,名称
  • 地址->Id、街道、街道编号、客户Id
  • 销售->地址ID,周,总计
我想得到每周的总销售额和客户(同时显示地址详细信息)。我提出了这个问题

SELECT a.Name, b.Street, b.StreetNo, c.Week, SUM (c.Total) as Total
FROM Customers a
    INNER JOIN Addresses b ON a.Id = b.CustomerId
    INNER JOIN Sales c ON b.Id = c.AddressId
GROUP BY a.Name, c.Week, b.Street, b.StreetNo
即使我的SQL技能几乎为零,它看起来也在发挥作用。但是现在,我希望能够在某个客户在某一周内没有销售时显示0(周仅为整数)。我想知道我是否应该在销售表中得到周数的不同值,然后循环它们(不确定如何循环)

有什么帮助吗


谢谢

请尝试以下操作

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;
我在三处修改了你的陈述。首先,我将您的加入更改为
Sales
右加入
。这将像与
内部联接一样联接,但它也将保留
联接
右侧表中左侧没有匹配记录或记录组的记录,并将
NULL
值放置在结果数据集的字段中,这些字段将来自
联接
左连接
的工作方式相同,但保留左侧表中的任何额外记录

我已从您幸存的
内部联接中删除了
内部联接
这个词。如果
连接
前面没有连接类型,则执行
内部连接
JOIN
internaljoin
都被认为是正确的,但是主流的协议似乎是将
internal
排除在外,而RDBMS允许将其排除在外(SQL Server就是这样做的)。你选择哪一个仍然完全取决于你——我把它留在这里是为了说明目的

第三个变化是,我添加了一个
CASE
语句,该语句测试
Total
字段是否包含
NULL
值,如果该客户在该周没有销售,该值就会出现。如果是,那么
SUM()
将返回一个
NULL
,因此
CASE
语句将返回一个
0
。如果
Total
不包含
NULL
值,则执行该分组的
Total
所有值的
SUM()

请注意,我假设
Total
不会有任何
NULL
值,而不是来自
右连接的
值。如果这个假设不正确,请告诉我

还请注意,我假设
销售
表中的
客户
不会缺少
,或者如果有,您不想列出这些周。如果这个假设不正确,请再次通知我


如果您有任何问题或意见,请随时发表相应的意见。

使用
交叉连接
为所有客户和周生成行。然后使用
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
对于
地址
客户
您应该生成周数,而不是使用
不同的
。这在性能和可靠性方面更好。然后在Sales表上使用
左联接
,而不是
内部联接

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]

我承认您在这里尝试了什么,不幸的是,第6行中的计数引发了一个异常,因为它既不在聚合函数中,也不在子句的组中。您肯定有一点,在我的情况下,虽然distinct更有意义,因为我没有所有的周,但它们的子集和distinct将更适合。