复杂的 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 查询的主要内容,如果未能解决你的问题,请参考以下文章

SQL之复杂查询与视图

springcloud 复杂sql查询

对 Linq 的复杂 SQL 查询

MySQL常用SQL(含复杂SQL查询)

复杂的 SQL 查询或查询

#yyds干货盘点#SQL 子查询