按月末 - 年初至今在两个日期之间计算行数

Count rows by monthend - year to date falling between two dates

提问人:Shadrack 提问时间:10/3/2023 最后编辑:Shadrack 更新时间:10/3/2023 访问量:77

问:

我正在尝试创建一个查询,该查询可以返回截至上个月最后一天的两个日期之间的行计数,以及运行查询时当前月份 (MTD) 的计数。例如,假设我今天需要这份报告。我可以使用一年中每个月的查询(过去的条件,而不是未来的条件)手动创建报告。1月、2月、3月、4月等我使用 date1<月底 (eom) 和 date2>eom。

但是,我想创建一个查询,该查询会自动为我提供行数,其中 date1 在 eom 之前,date2 在 eom 之后(或 date2 为 null)。我想也许有某种方法可以使用 systemdate 和 dateadd 向后计数,但我不知道如何将它们放在一起以提供截至每个月最后一天的所有过去 12 个月。每个月的最后一天不是直接存储在我需要从中提取的表中的列。

有一个日期维度表,但如果有必要,我不确定如何将其包含在我提取日期的表中。小于日期和大于日期表示我需要在几个月或一年内提取计数的“截至日期”。

Select count(column1) as "September Count"
From table a
 Left Outer Join a table b on a.pk=b.pk
Where a.date1 <= '2023-09-30 00:00:00'
 AND b.date2 > '2023-09-30 00:00:00'
        OR date2 is null)


UNION ALL

Select count(a.column1) as "August Count"
,b.otherstuff as "Important"
,a.OtherStuff as "Also Important"
From table a
Left Outer Join table2 b on a.pk=b.pk
Where a.date1 <= '2023-08-31 00:00:00'
 AND (b.date2 > '2023-08-31 00:00:00'
        OR b.date2 is null)

等。。。。。

结果应该是这样的:

八月计数 |9月盘点|重要 |同样重要 46890 39640 |资料 |其他东西

如果我需要提供更多详细信息,请告诉我。该数据库是专有的,但如有必要,我可以发布更多虚假信息进行解释。非常感谢您的帮助!

我知道我可以为每个月创建一个查询并将它们合并在一起,但我想要一种更好的方法来做到这一点,它可以更加自动化和更少的手动。

结果应该是这样的:

八月计数 9月计数 重要 同样重要
46890 39640 东西 其他东西

计数 重要 同样重要
46890 八月 东西 其他东西
39640 九月 东西 2 2 其他东西

如果我需要提供更多详细信息,请告诉我。该数据库是专有的,但如有必要,我可以发布更多虚假信息进行解释。非常感谢您的帮助!

示例数据

日期 事件日期2 代表 事件描述 添加方式
2023-07-26 2023-08-05 编号: 22E16587 “母鹿,简” 事件类型 皮尔尔
2023-01-17 2023-09-12 编号:23E13561 “小奔波” 事件类型 皮尔尔
2023-01-14 2023-09-29 编号:23E16154 “史密斯,迈克” 事件类型2 服务器
2023-02-09 2023-08-16 编号: 23E21553 “母鹿,瑞恩” 事件类型 皮尔尔
2022-02-16 2023-09-27 编号:23E22373 事件类型 皮尔尔
2023-02-25 2023-10-02 编号:23E23905 “史密斯,迈克” 事件类型 皮尔尔
2023-03-11 2023-10-02 编号:23E24451 “史密斯,迈克” 事件类型 服务器
2023-04-11 2023-10-02 编号:23E24476 “史密斯,迈克”, 事件类型 服务器
2023-05-12 2023-10-02 编号:23E24778 “史密斯,迈克”, 事件类型 服务器
2023-06-12 2023-09-22 编号: 23E24830 事件类型 服务器

下面是与现有查询更相似的内容,但一次只有一个月:

Select 
a.StatusDescr as "Status"
,Count(distinct a.CaseNbr) as "September Count"
From CRM a
 Inner JOIN Event b on a.AcctID=b.AcctID
 Left Outer Join Meeting c on a.AcctID=c.AcctID
 Left Outer Join Party e on a.AcctID=e.AcctID
 Left Outer Join Service g on a.AcctID=g.AcctID
 LEFT OUTER JOIN Closure h on a.AcctID=h.AcctID
 Inner JOIN User f on b.Event_User=f.Name
  Where  
    b.eventdate<= '2023-09-30 00:00:00'
   AND h.closedate > '2023-09-30 00:00:00'
    (a.AcctType='SMMS'
    or a.AcctType='SGHOV'
    or a.AcctType='SMXD')
   AND (a.LocID='219' 
    or a.LocID='200' 
    or a.LocID='260')
   AND (b.EventTYPE='1252'       
    OR b.EventTYPE='1225'
    OR b.EventCd='SMRESP')
   AND b.DeletedFlag='No'
   AND h.CurrentFlag='yes'
   AND a.statusdescr='open'
 GROUP BY a.statusdescr asc;
Server SQL Server-2014 分区 滚动计算

评论

0赞 Xedni 10/3/2023
你能提供任何样本数据吗?假数据对于提出查询来帮助您来说是完全可以的,只要您知道您希望从虚假数据中获得的结果是什么

答:

0赞 Xedni 10/3/2023 #1

我只会确定您感兴趣的时间段,并在按这些日期进行筛选时按日期对数据进行分组。

以下是分布在当前日期前后 180 天内的完整数据:

drop table if exists #tab
create table #tab
(
    SK int identity(1,1),
    Dt date,
    Value int
)

insert into #Tab
(
    Dt,
    Value
)
select top 100000
    dateadd(day, checksum(newid()) % 180, getdate()),
    abs(checksum(newid()))
from sys.all_objects a, sys.all_objects b

接下来,您要查找当前月份和上个月的所有行。您可以通过多种方式执行此操作;您可以使用变量(就像我所做的那样)或内联语句来做到这一点。您可以检查日期列的年份和月份值,但这在年末会变得有点棘手,因为如果您在 Januaray 中运行它,则必须回溯您的年份。所以我认为最简单的方法是我在这里展示的,你可以得到上个月初到本月底之间的所有行。

declare 
    @Now date = getdate(),
    @StartOfPreviousMonth date,
    @EndOfPreviousMonth date,    
    @StartOfCurrentMonth date,
    @EndOfCurrentMonth date

select
    @EndOfCurrentMonth = eomonth(getdate()),
    @StartOfCurrentMonth = datefromparts(year(@Now), month(@Now), 1),
    @StartOfPreviousMonth = dateadd(month, -1, @StartOfCurrentMonth),
    @EndOfPreviousMonth = eomonth(@StartOfPreviousMonth)

select
    StartOfCurrentMonth = @StartOfCurrentMonth,
    EndOfCurrentMonth = @EndOfCurrentMonth,
    StartOfPreviousMonth = @StartOfPreviousMonth,
    EndOfPreviousMonth = @EndOfPreviousMonth

select
    _Year = year(dt),
    _Month = month(dt),
    Ct = count(1)
from #tab
where Dt between @StartOfPreviousMonth and  @EndOfCurrentMonth
group by year(Dt), month(dt)

关于格式化的最后一点,通常,我不喜欢将这样的数据透视到单行上。如果列名称中包含月份,则无法轻松重新运行相同的查询,并让应用知道要查找的字段(即,您的应用是否必须查找列的所有可能的月份名称?我建议将其保留为两行。但是,如果这是不可能的,我建议将字段命名为“CurrentMonthTotal”和“PreviousMonthTotal”,因为至少这样可以预测列的命名方式。

因此,如果您想将它们放在同一行上,也许可以尝试如下:

select
    PreviousMonthCt = max(case when _Month = month(@StartOfPreviousMonth) then Ct else null end),
    CurrentMonthCt =  max(case when _Month = month(@StartOfCurrentMonth) then Ct else null end)
from
(
    select
        _Year = year(dt),
        _Month = month(dt),
        Ct = count(1)
    from #tab
    where Dt between @StartOfPreviousMonth and  @EndOfCurrentMonth
    group by year(Dt), month(dt)
) a

评论

0赞 Shadrack 10/3/2023
不同的月份不一定必须在同一行。我试图展示一个月份在不同行上的位置的例子,但惨遭失败,因为这是我第一次在这里发帖。
0赞 Shadrack 10/6/2023
感谢您的回复。在这种情况下,我似乎需要收集所需的数据并创建一个临时表。然后根据新表中的“快捷方式”和数据进行分组,将其分解为我需要的时间段。这是对的吗?我还是个新手。记录或行的数量很容易达到每年 100k 以上或每月 10k 以上。有没有办法列出声明的所有时间段,然后在一次运行中将查询应用于每个时间段,而无需复制查询或创建临时表?运行时间会不会太长?
0赞 Xedni 10/12/2023
只要您不在 Atari 2600 上运行服务器,100k 行就不应该太担心。这是非常主观的(并且不能让你自由地编写糟糕的代码),但是当事情进入数百万行时,我倾向于开始仔细考虑我正在移动多少数据。
0赞 Shadrack 10/13/2023
非常感谢您的帮助。我最终确实使用了您的代码 + 多个查询和联合来将比较月份放在一起。与其他不需要在两个日期之间计算事件的例子相比,你的方式要容易得多(与我之前所做的相比),而且速度也快得多。合并几个查询,但日期计算完美无缺,并且很容易在报告工具中分离结果。我学到了一些有价值的东西。我把你的答案标记为答案,但我没有足够的声誉来展示它。