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

时间:2015-01-09 17:34:38

标签: sql sql-server missing-data

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

3 个答案:

答案 0 :(得分:0)

您需要列出所有年份的另一个表 - 您可以动态创建此表,或者在数据库中添加一个...从中加入。因此,如果您有一个名为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

答案 1 :(得分:0)

您需要一个提供年份数字的数据源。你不能凭空制造它们。假设您有一个表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,但它为&#39} ; s与没有连接谓词的INNER JOIN相同,这是Access要求的)
  • 为了使结果行多年没有付款,您必须执行外部联接(在本例中为LEFT JOIN)。如果(基本客户,年份)组合没有关联订单,则联接结果的其余列将为NULL
  • 我从Base_CustomerT中选择了CustomerId,因为如果您从NULL中选择了SO_SalesOrderT,则有时会获得Nz()
  • 我使用访问NULL功能将HAVING付款金额转换为0(与未付款的年份对应的行)
  • 我将您的WHERE子句转换为WHERE子句。在这种特定情况下,它在语义上是等效的,并且它会更高效,因为{<1}}过滤器在组形成之前应用,并且因为它允许从某些列中省略某些列GROUP BY条款。
  • 按照Hogan的例子,我会在数据覆盖的整个范围之外过滤数年。或者,通过确保表Intersting_year仅包含您想要结果的年份数字,您可以在没有该过滤条件及其子查询的情况下实现相同的效果。

更新:将查询修改为不同的,但逻辑上相同的&#34;类似这样的&#34;我希望Access会更好。除了添加一堆括号外,主要区别在于将LEFT JOIN的左右操作数都放入子查询中。这与解决Access&#34;模糊外连接的共识建议一致&#34;错误。

答案 2 :(得分:0)

感谢John的帮助。我找到了一个适合我的解决方案。它看起来很安静,但我从中学到了很多东西。如果您对此感兴趣,请立即了解它。

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