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

问题描述 投票:1回答:1

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

  • OrderYear - 订单下达的年份
  • CustomerCategoryName - 显示在表Sales.CustomerCategories
  • CustomerCount - 为每个CustomerCategoryNameOrderYear下订单的唯一客户数量
  • OrderCount - 为每个CustomerCategoryNameOrderYear下达的订单数量
  • 销售 - 来自订单的小计,根据表QuantityUnitPriceSales.OrderLines计算得出
  • AverageSalesPerCustomer - 每个CustomerCategoryNameOrderYear的每位客户的平均销售额

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

我尝试解决方案:

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似乎是正确的。但是,我不相信我的CustomerCount是正确的,我的SalesAverageSalesPerCustomer似乎有点偏离。没有任何客户和订单的Categories没有显示在我的结果中。

我的计数是否已关闭以及没有任何客户的类别被省略的原因是因为它们只有空值?我相信问题在于寻找所有类别。

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

任何帮助都会受到赞赏,因为我是SQL的新手,而Joins是一个非常难以理解的概念。

sql-server tsql join
1个回答
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子句来只选择一个公司...然后去看看细节......看看如果它通过气味测试。限制它们时,评估结果要容易得多。同样,您可以出于同样的原因将客户添加到选择列表和外部分组。实验是关键。

© www.soinside.com 2019 - 2024. All rights reserved.