SQL查询多个联接的意外结果

时间:2018-09-14 22:20:27

标签: sql-server tsql join

我的任务是编写一个查询,该查询将返回每个客户类别和年份的销售信息。结果集中所需的列为:

  • OrderYear -下订单的年份
  • CustomerCategoryName -出现在表Sales.CustomerCategories
  • CustomerCount -每个CustomerCategoryNameOrderYear的唯一客户数量
  • OrderCount -为每个CustomerCategoryNameOrderYear
  • 下的订单数
  • 销售-根据表Quantity的{​​{1}}和UnitPrice计算的下达订单的小计
  • AverageSalesPerCustomer -每个Sales.OrderLinesCustomerCategoryName
  • 的每位客户的平均销售额

结果应按升序排列,首先是订单年份,然后是客户类别名称。

我试图解决的问题:

OrderYear

我的SELECT CC.CustomerCategoryName, YEAR(O.OrderDate) AS OrderYear, COUNT(DISTINCT C.CustomerID) AS CustomerCount, COUNT(DISTINCT O.OrderID) AS OrderCount, SUM(OL.Quantity * OL.UnitPrice) AS Sales, SUM(OL.Quantity * OL.UnitPrice) / COUNT(DISTINCT C.CustomerID) AS AverageSalesPerCustomer FROM Sales.CustomerCategories CC INNER JOIN Sales.Customers C ON C.CustomerCategoryID = CC.CustomerCategoryID INNER JOIN Sales.Orders O ON O.CustomerID = C.CustomerID INNER JOIN Sales.OrderLines OL ON OL.OrderID = O.OrderID GROUP BY CC.CustomerCategoryName, YEAR(O.OrderDate) ORDER BY YEAR(O.OrderDate), CC.CustomerCategoryName; 似乎正确。但是,我不相信我的OrderCount是正确的,并且我的CustomerCountSales似乎相去甚远。没有任何客户和订单的AverageSalesPerCustomer不会显示在我的结果中。

是因为我的计数没有值,并且没有任何客户的类别被忽略是因为它们只有空值?我相信问题正在寻找所有类别。

我正在使用Microsoft的WideWorldImporters示例表。

由于我是SQL新手,所以任何帮助将不胜感激,而Joins是我很难理解的概念。

1 个答案:

答案 0 :(得分:1)

当前,您只获取订单明细中存在的数据...而不获取不存在的订单的任何数据。通常,这是通过外部联接而不是内部联接和isnull(possiblyNullValue,replacementValue)完成的。

此外,当您按年份(o.OrderDate)分组时,您的订单加入没有按年份进行区分...可能会在每个报告期内获取每个客户的所有年份的数据。

因此,让我们首先得出报告期...并确保我们以此为基础得出结果:

select distinct year(o.OrderDate) from Sales.Orders

但是,实际上,您想要所有类别和所有年份...因此您可以将它们结合起来以获得真实基础:

select
  cc.CustomerCategoryId,
  cc.CustomerCategoryName, 
  year(o.OrderDate)
from 
  Sales.Orders o
    cross join
  Sales.CustomerCategories cc
group by
  cc.CustomerCategoryId,
  cc.CustomerCategoryName, 
  year(o.OrderDate)

现在,您想将此混乱情况合并到其余查询中。有两种方法可以执行此操作...一种是使用with子句...但是有时将基础查询用括号括起来并像表一样使用会更容易:

select
  cy.CustomerCategoryName,
  cy.CalendarYear,
  count(distinct c.CustomerId) CustomerCount,
  isnull(sum(ol.UnitPrice * ol.Quantitiy),0.0) Sales,
  isnull(sum(ol.UnitPrice * ol.Quantitiy) / count(distinct c.CustomerId),0.0) AverageSalesPerCustomer
from
  (
    select
      cc.CustomerCategoryId,
      cc.CustomerCategoryName, 
      year(o.OrderDate) CalendarYear --> must name calc'd cols in virtual tables
    from 
      Sales.Orders o
        cross join
      Sales.CustomerCategories cc
    group by
      cc.CustomerCategoryId,
      cc.CustomerCategoryName, 
      year(o.OrderDate)
  ) as cy --> cy is the "Category Years" virtual table
    left outer join
  Sales.Customers c
    on cy.CustomerCategoryId = c.CustomerCategoryId
    left outer join
  Sales.Orders o
    on 
      c.CustomerId = o.CustomerId          --> join on customer and year
      and                                  --> to make sure we're only getting 
      cy.CalendarYear = Year(o.OrderDate)  --> orders in the right year
    left outer join
  Sales.OrderLines ol
    on o.OrderId = ol.OrderId
group by
  cy.CalendarYear,
  cy.CustomerCategoryName
order by
  cy.CalendarYear,
  cy.CustomerCategoryName

顺便说一句...使您的查询变得混乱起来以选择某些子集...例如,您可以添加where子句以仅选择一家公司...然后查看详细信息。看它是否通过了气味测试。当您限制结果时,评估结果要容易得多。同样,出于相同的原因,您可以将客户添加到选择列表和外部分组中。实验是关键。