复杂的 SQL 查询
Posted
技术标签:
【中文标题】复杂的 SQL 查询【英文标题】:Complex SQL query 【发布时间】:2011-08-04 15:52:11 【问题描述】:我有一个表格,用于跟踪从我的服务器上的应用程序发送的电子邮件。我想写一个查询,显示在特定时间段内每个应用程序发送了多少电子邮件。这是表格:
----------------------------------------------------------
| emailID | SentDT | ApplicationName |
----------------------------------------------------------
| 1 | 2011-08-04 14:43:31.080 | Term Form |
----------------------------------------------------------
| 2 | 2011-08-04 13:59:46.062 | Term Form |
----------------------------------------------------------
| 3 | 2011-08-03 10:38:15.015 | Request Form |
----------------------------------------------------------
| 4 | 2011-08-03 05:52:29.005 | Term Form |
----------------------------------------------------------
| 5 | 2011-08-01 19:58:31.094 | Recruiting Form |
----------------------------------------------------------
我想查看今天、过去 24 小时、过去 7 天、本月、上个月、所有时间发送的电子邮件数量。
我知道如何自己完成每个查询,但我不知道如何在一次访问数据库中完成。
例如:
--------------------------------------------------------------
| ApplicationName | Today | Last24 | Last7days | ThisMonth |
--------------------------------------------------------------
| Term Form | 2 | 5 | 10 | 19 |
--------------------------------------------------------------
| Request Form | 9 | 18 | 36 | 75 |
--------------------------------------------------------------
| Recruiting Form | 15 | 35 | 100 | 250 |
--------------------------------------------------------------
我尝试对每个时间子集使用嵌套选择,但我不能在嵌套选择中使用 group by
。我的查询没有产生结果:
select COUNT(emailID), ApplicationName, (select COUNT(emailID) from emaillog where SentDT > '08/02/2011') as TwoDaysAgo
from emaillog
group by ApplicationName
order by ApplicationName
【问题讨论】:
必须做什么?您遇到什么错误? 【参考方案1】:我认为预先进行所有日期计算要容易得多,然后您可以使用逻辑名称引用局部变量,而不是在查询逻辑中嵌入所有日期差异/大小写等计算。
在这里做了几个假设。 (1) EmailLog 中没有未来的数据 (2) “过去 7 天”是指今天和之前的整整 6 天。我还包括了一个总计 - 即使它没有在您想要的输出中列出,但您似乎试图在子查询之外使用 COUNT()
来获取它。
DECLARE @now SMALLDATETIME = SYSDATETIME();
DECLARE @today DATE = @now,
@24hrsago SMALLDATETIME = DATEADD(DAY, -1, @now);
DECLARE @7daysago DATE = DATEADD(DAY, -6, @today),
@ThisMonth DATE = DATEADD(DAY, 1-DATEPART(DAY, @today), @today);
--SELECT @now, @today, @24hrsago, @7daysago, @ThisMonth;
WITH d AS
(
SELECT ApplicationName, c = COUNT(*)
FROM EmailLog
GROUP BY ApplicationName
),
g AS
(
SELECT
ApplicationName,
[Today] = SUM(CASE WHEN SentDt >= @today THEN 1 ELSE 0 END),
[Last24] = SUM(CASE WHEN SentDt >= @24hrsago THEN 1 ELSE 0 END),
[Last7Days] = SUM(CASE WHEN SentDt >= @7daysago THEN 1 ELSE 0 END),
[ThisMonth] = SUM(CASE WHEN SentDt >= @ThisMonth THEN 1 ELSE 0 END)
FROM EmailLog
GROUP BY ApplicationName
)
SELECT d.ApplicationName,
Total = d.c,
[Today] = COALESCE(g.[Today], 0),
[Last24] = COALESCE(g.[Last24], 0),
[Last7days] = COALESCE(g.Last7days, 0),
[ThisMonth] = COALESCE(g.ThisMonth, 0)
FROM d LEFT OUTER JOIN g
ON d.ApplicationName = g.ApplicationName;
编辑
如果我的假设是错误的,并且您不需要按应用程序名称计算的总数,则查询会变得更加简单:
DECLARE @now SMALLDATETIME = SYSDATETIME();
DECLARE @today DATE = @now,
@24hrsago SMALLDATETIME = DATEADD(DAY, -1, @now);
DECLARE @7daysago DATE = DATEADD(DAY, -6, @today),
@ThisMonth DATE = DATEADD(DAY, 1-DATEPART(DAY, @today), @today);
SELECT ApplicationName,
[Today] = SUM(CASE WHEN SentDt >= @today THEN 1 ELSE 0 END),
[Last24] = SUM(CASE WHEN SentDt >= @24hrsago THEN 1 ELSE 0 END),
[Last7Days] = SUM(CASE WHEN SentDt >= @7daysago THEN 1 ELSE 0 END),
[ThisMonth] = SUM(CASE WHEN SentDt >= @ThisMonth THEN 1 ELSE 0 END)
FROM EmailLog
GROUP BY ApplicationName;
订购当然是可选的。
【讨论】:
【参考方案2】:尝试:
Select ApplicationName, COunt(*) numEmails
From table
where SentDT Between @startDateTime and @EndDateTime
Group By ApplicationName
注意:startDateTime 和 EndDateTime 是对要处理的记录的普遍限制。
如果您还想围绕指定的日期时间范围建立存储桶,您只需通过表达式在另一个组中定义这些日期时间范围存储桶(并在 select 子句中输出相同的表达式......例如,例如日期时间范围是日历月...
Select DateAdd(month, DateDiff(month, 0, SentDT), 0) CalMonth,
ApplicationName, Count(*) numEmails
From table
where SentDT Between @startDateTime and @EndDateTime
Group By DateAdd(month, DateDiff(month, 0, SentDT), 0),
ApplicationName
【讨论】:
这就是我现在所拥有的,但它是每个时间段的其中之一。我想将它们全部合并到一个查询中 这没有给出想要的结果... [startDateTime] 和 [EndDateTime] 应该是列吗?今天、过去 24 小时、过去 7 天和本月在您的输出中表示在哪里?这将返回一行两列。 [StartDateTime 和 enddatetime 是边界限制值【参考方案3】:这样的事情应该可以解决问题
select
ApplicationName,
sum(case when daterange = 0 then cnt else 0 end) as Today,
sum(case when daterange = 1 then cnt else 0 end) as yesterday,
sum(case when daterange <=2 then cnt else 0 end) as Week,
sum(case when daterange <=3 then cnt else 0 end) as month,
sum(cnt) as AllTime
from
(select
ApplicationName,
case
when days = 0 then '0'
when days = 1 then '1'
when days <= 7 then '2'
when days <= 30 then '3'
else 4
end as
DateRange,
Count(emailid) cnt
from
(select ApplicationName, EmailID, datediff(dd, SentDT, getdate()) as Days
from
dbo.[YourTableGoesHere]
) as foo
Group by
ApplicationName,
case when days < 1 then '0'
when days = 1 then '1'
when days <= 7 then '2'
when days <= 30 then '3'
else 4
end) as bar
group by
ApplicationName
【讨论】:
我不认为使用这种方法可以获得“过去 24 小时”,因为 datediff 只考虑日期边界。以上是关于复杂的 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章