在 Group By 查询中包含缺失的年份

Include missing years in Group By query

我是 Access 和 SQL 编程的新手。我正在尝试执行以下操作:

Sum(SO_SalesOrderPaymentHistoryLineT.Amount) AS [Sum Of PaymentPerYear]

并按年份分组,即使某些年份没有金额。我想将这些年也列出来作为一份带有图表的报告。我不确定这是否可行,但感谢您的每一点帮助。

到目前为止我的代码如下:

SELECT
  Base_CustomerT.SalesRep,
  SO_SalesOrderT.CustomerId,
  Base_CustomerT.Customer,
  SO_SalesOrderPaymentHistoryLineT.DatePaid,
  Sum(SO_SalesOrderPaymentHistoryLineT.Amount) AS [Sum Of PaymentPerYear]

FROM
  Base_CustomerT
  INNER JOIN (
    SO_SalesOrderPaymentHistoryLineT
      INNER JOIN SO_SalesOrderT
        ON SO_SalesOrderPaymentHistoryLineT.SalesOrderId = SO_SalesOrderT.SalesOrderId
  ) ON Base_CustomerT.CustomerId = SO_SalesOrderT.CustomerId

GROUP BY
  Base_CustomerT.SalesRep,
  SO_SalesOrderT.CustomerId,
  Base_CustomerT.Customer,
  SO_SalesOrderPaymentHistoryLineT.DatePaid,
  SO_SalesOrderPaymentHistoryLineT.PaymentType,
  Base_CustomerT.IsActive

HAVING
  (((SO_SalesOrderPaymentHistoryLineT.PaymentType)=1)
  AND ((Base_CustomerT.IsActive)=Yes))

ORDER BY
  Base_CustomerT.SalesRep,
  Base_CustomerT.Customer;

您需要另一个 table 并列出所有年份 -- 您可以即时创建它或在数据库中创建一个...从中加入。因此,如果您有一个名为 alltheyears 的 table,其中一个名为 y 的列刚刚列出了年份,那么您可以使用如下代码:

 WITH minmax as
 (
    select min(year(SO_SalesOrderPaymentHistoryLineT.DatePaid) as minyear,
           max(year(SO_SalesOrderPaymentHistoryLineT.DatePaid) as maxyear)
    from SalesOrderPaymentHistoryLineT
 ), yearsused as
 (
    select y
    from alltheyears, minmax
    where alltheyears.y >= minyear and alltheyears.y <= maxyear
 )
 select *
 from yearsused
 join ( -- your query above goes here! -- ) T 
    ON year(T.SO_SalesOrderPaymentHistoryLineT.DatePaid) = yearsused.y

您需要一个能够提供年份数字的数据源。你不能凭空制造它们。假设你有一个 table Interesting_year,只有一个列 year,填充了 2000 到 2050 之间的每个不同的整数,你可以这样做:

SELECT
  base.SalesRep,
  base.CustomerId,
  base.Customer,
  base.year,
  Sum(NZ(data.Amount)) AS [Sum Of PaymentPerYear]

FROM
  (SELECT * FROM Base_CustomerT INNER JOIN Year) AS base
  LEFT JOIN
  (SELECT * FROM
    SO_SalesOrderT
    INNER JOIN SO_SalesOrderPaymentHistoryLineT
      ON (SO_SalesOrderPaymentHistoryLineT.SalesOrderId = SO_SalesOrderT.SalesOrderId)
  ) AS data
    ON ((base.CustomerId = data.CustomerId)
      AND (base.year = Year(data.DatePaid))),

WHERE
  (data.PaymentType = 1)
  AND (base.IsActive = Yes)
  AND (base.year BETWEEN
    (SELECT Min(year(DatePaid) FROM SO_SalesOrderPaymentHistoryLineT)
    AND (SELECT Max(year(DatePaid) FROM SO_SalesOrderPaymentHistoryLineT))

GROUP BY
  base.SalesRep,
  base.CustomerId,
  base.Customer,
  base.year,

ORDER BY
  base.SalesRep,
  base.Customer;

注意以下几点:

  • 修改后的查询首先形成 BaseCustomerTInteresting_year 的笛卡尔积,以便获得与每年相关联的基本客户数据(有时称为 CROSS JOIN,但它是与没有连接谓词的 INNER JOIN 相同,这是 Access 所要求的)
  • 为了获得没有付款的年份的结果行,您必须执行外部联接(在本例中为 LEFT JOIN)。如果(基本客户,年份)组合没有关联订单,则连接结果的其余列将为 NULL.
  • 我从 Base_CustomerT 中选择 CustomerId,因为如果您从 SO_SalesOrderT 中选择有时会得到 NULL,就像在开始查询中那样
  • 我正在使用 Access Nz() 函数将 NULL 付款金额转换为 0(来自与没有付款的年份对应的行)
  • 我将您的 HAVING 子句转换为 WHERE 子句。在这种特殊情况下,这在语义上是等效的,并且会更有效,因为 组形成之前应用了 WHERE 过滤器,并且因为它允许从中省略某些列GROUP BY 子句。
  • 按照 Hogan 的示例,我过滤掉了您的数据涵盖的总体范围之外的年份数据。或者,通过确保 table Intersting_year 仅包含您想要结果的年份数字,您可以在没有该过滤条件及其子查询的情况下实现相同的效果。

更新:将查询修改为不同但逻辑上等效的 "something like this",我希望 Access 会更好。除了添加一堆括号外,主要区别在于将 LEFT JOIN 的左右操作数都变成了子查询。这与解决 Access "ambiguous outer join" 错误的共识建议一致。

谢谢约翰的帮助。我找到了适合我的解决方案。它看起来很不同,但我从中学到了很多东西。如果你感兴趣,这里是它现在的样子。

SELECT DISTINCTROW
  Base_Customer_RevenueYearQ.SalesRep,
  Base_Customer_RevenueYearQ.CustomerId,
  Base_Customer_RevenueYearQ.Customer,
  Base_Customer_RevenueYearQ.RevenueYear,
  CustomerPaymentPerYearQ.[Sum Of PaymentPerYear]
FROM
  Base_Customer_RevenueYearQ
  LEFT JOIN CustomerPaymentPerYearQ
    ON (Base_Customer_RevenueYearQ.RevenueYear = CustomerPaymentPerYearQ.[RevenueYear])
      AND (Base_Customer_RevenueYearQ.CustomerId = CustomerPaymentPerYearQ.CustomerId)
GROUP BY
  Base_Customer_RevenueYearQ.SalesRep,
  Base_Customer_RevenueYearQ.CustomerId,
  Base_Customer_RevenueYearQ.Customer,
  Base_Customer_RevenueYearQ.RevenueYear,
  CustomerPaymentPerYearQ.[Sum Of PaymentPerYear]
;