【问题标题】:Include missing years in Group By query在 Group By 查询中包括缺失的年份
【发布时间】:2015-01-09 17:34:37
【问题描述】:

我是 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;

【问题讨论】:

  • 您需要另一个列出所有年份的表——您可以即时创建它,也可以在数据库中创建一个...从那里加入。
  • 你想要怎样的结果?是否要按升序显示年份(2000、2001、2002、2003、... 2015)与每年的销售额?
  • 所以SO_SalesOrderPaymentHistoryLineT.DatePaid 是年号?我猜这是一个约会,不会让你获得每年的团体。

标签: sql sql-server missing-data


【解决方案1】:

您需要另一个列出所有年份的表——您可以即时创建它或在数据库中创建一个...从该表连接。因此,如果你有一个名为 alltheyears 的表,其中有一列名为 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

【讨论】:

    【解决方案2】:

    您需要一个能够提供年份数字的数据源。你不能凭空制造它们。假设您有一个表 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 的示例,我过滤掉了超出您数据涵盖的总体范围的年份的数据。或者,您可以通过确保表 Intersting_year 仅包含您想要的结果的年份数字来实现相同的效果而无需该过滤条件及其子查询。

    更新:将查询修改为不同但在逻辑上等效的“类似这样的东西”,我希望 Access 会更喜欢。除了添加一堆括号外,主要区别在于将LEFT JOIN 的左右操作数都变成了子查询。这与解决 Access“模糊外部连接”错误的共识建议一致。

    【讨论】:

    • 首先感谢你们的快速回复。约翰,我知道我必须创建一个多年的新表,但我无法让它与这个 LEFT JOIN 一起工作。错误消息是说 OUTER JOIN 太模糊了。
    • Access 的“Jet”SQL 实现有点……奇特……我不准备对其进行测试。但是,为了解决模棱两可的外部联接问题,我确实在特定查询中用另一个削减更新了我的答案。
    • 感谢约翰的帮助。我找到了一个适合我的解决方案。它看起来很安静,但我从中学到了很多。如果你有兴趣,这里是它现在的样子。
    【解决方案3】:

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

    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]
    ;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-12-21
      • 2010-10-05
      • 1970-01-01
      • 1970-01-01
      • 2014-12-26
      • 2019-04-30
      • 1970-01-01
      相关资源
      最近更新 更多