【问题标题】:Aggregate SQL query grouping by month按月聚合 SQL 查询分组
【发布时间】:2011-08-10 13:32:48
【问题描述】:

我有一个交易数据库 (Access 2007),它以每小时、每天和每月的间隔记录。我想以一种有意义的方式查看它们(而不是按小时计算的能源使用量,这是它的存储方式,我想要 2011 年 1 月的所有使用量)。我要操作的表具有这种通用格式:

CustID|CustomerCode|TransactionDate|(Transaction Hour depending on table)|Usage

所以如果我想拿一张这样的桌子并制作一个看起来像这样的视图

BillingPeriod|Usage(mWh)
1/2011     |500
2/2011     |600
3/2011     |700
etc

我该怎么做呢?交易日期可以是任意日期,交易时间可以是1-24。查询本身似乎并不难,类似于:

SELECT TransactionDate, SUM(Usage)
FROM UsageTable
Where (TransactionDate Between [Some Start Date] AND[Some End Date])
GROUP BY TransactionDate;

问题在于格式。我显然不能按事务日期对我想要的结果进行分组,我只是写了它,所以查询在语义上是正确的。也许我可以做类似的事情

SELECT Mid(TransactionDate,0,2) + Mid(TransactionDate, 6, 4)?

任何帮助将不胜感激

【问题讨论】:

  • 您需要按 TransactionDate 分组,但您必须将日期转换为 MM/YYYY,以便您的分组正常工作。在 SQL Server 中,这很容易做到,但我注意到您要求 ms-access,所以我不确定是否支持相同的日期时间函数。在 SQL Server 中,您可以使用 datepart 函数获取月份和年份,DATEPART(yyyy, TransactionDate) 将获取年份,DatePart(mm,TransactionDate) 将获取月份。因此,您可以按类型转换 (Datepart(yyyy,TransactionDate) as varchar(4))+'-'+(Datepart(mm,TransactionDate) as varchar(2))
  • 我刚刚确认,access中支持MONTH()和YEAR()函数
  • 将值转换为字符串,然后将它们连接起来,以完成日期运算,是 Bad Practice! 要执行您建议的操作,只需使用 @ 987654325@(使用SQL Server时,将m替换为MONTH,示例为MS Access版本。)
  • @Dems 你说的日期算术在哪里?你是在调用 GROUP BY 日期算术吗?
  • @HansUp - 将日期或日期时间转换为仅限月份的值是日期算术。

标签: sql ms-access


【解决方案1】:

您似乎需要按月份和年份进行分组。否则,您将合并 2010 年 1 月和 2011 年 1 月:

SELECT YEAR(TransactionDate), MONTH(TransactionDate), SUM(Usage)
FROM YourTable
WHERE (TransactionDate Between [Some Start Date] AND[Some End Date])
GROUP BY YEAR(TransactionDate), MONTH(TransactionDate)
ORDER BY YEAR(Created), MONTH(Created)

我不知道你的 SQL 版本是否有 MONTH 和 YEAR 函数,所以你可能必须使用 DATEPART。

【讨论】:

  • 每个人都试图用字符串来完成这个是怎么回事?字符串转换和连接引入了不必要的开销,可以使用可用的日期函数单独完成。
【解决方案2】:

为了避免转换为字符串、连接和转换回日期,请使用 DATEADD() 和 DATEDIFF()。

SELECT
  DATEADD("m", DATEDIFF("m", 0, TransactionDate), 0) AS TransactionMonth,
  SUM(Usage)                                         AS TotalUsage
FROM
  yourTable
WHERE
  TransactionDate BETWEEN <startDate> AND <endDate>
GROUP BY
  DATEADD("m", DATEDIFF("m", 0, TransactionDate), 0)
ORDER BY
  DATEADD("m", DATEDIFF("m", 0, TransactionDate), 0)

【讨论】:

  • DateAdd 和 DateDiff 都需要一个字符串表达式作为间隔参数。因此,要使这种方法起作用,您必须在 m 周围使用引号。如果没有引号,db 引擎会将 m 解释为参数并弹出“输入参数值”对话框。
【解决方案3】:

我通常使用Format([TransactionDate], "yyyy-mm"),因为它简单且分类良好。
作为另一种选择,您可以使用[TransactionDate]-Day([TransactionDate])+1,这会将每个日期移到其月份的第一天。优点是您仍然可以轻松地以任何您想要的方式对其进行格式化,或者在之后按季度或年度对其进行分组。

【讨论】:

  • 或者,您可以只使用可用的日期函数来避免与字符串相互转换的开销?
【解决方案4】:
SELECT MONTH(TransactionDate),YEAR(TransactionDate), SUM(Usage) 
FROM UsageTable 
Where (TransactionDate Between [Some Start Date] AND[Some End Date]) 
GROUP BY MONTH(TransactionDate),YEAR(TransactionDate);

【讨论】:

  • 我做了同样的事情,但你记得 WHERE 子句。我忘记了。
  • 那会给我 3 列,对吗?我可以将它们连接起来吗?
  • 你可以! SELECT CONVERT(VARCHAR,MONTH(TransactionDate)) + "/" + CONVERT(VARCHAR,YEAR(TransactionDate)) Period, SUM(Usage) FROM UsageTable Where (TransactionDate between [Some Start Date] AND[Some End Date]) GROUP BY CONVERT(VARCHAR,MONTH(TransactionDate)) + "/" + CONVERT(VARCHAR,YEAR(TransactionDate));
  • 啊! :( 请不要再教人们将值与字符串进行转换,一切都可以直接使用可用的日期函数来完成 :(
【解决方案5】:

类似...

SELECT Month(UsageTable.TransactionDate) & '/' & Year(UsageTable.TransactionDate) AS BillingPeriod, Sum(UsageTable.Usage) AS Usage
FROM UsageTable
WHERE (((UsageTable.TransactionDate) Between [Some Start Date] And [Some End Date]))
GROUP BY Month(UsageTable.TransactionDate) & '/' & Year(UsageTable.TransactionDate);

【讨论】:

  • 这隐式地将值转换为字符串,然后连接字符串,因此引入了不必要的隐藏开销。 (只需使用可用的日期函数即可完成,无需任何字符串连接。)