Sql server SQL-按月份和去年的字段值对记录进行计数和分组
我需要计算表“a”中记录的总数,其中“a”中的字段(比如“type”)有一个特定的值“v”。从所有这些a.type='v'的记录中,我需要对它们进行两次分组:首先按字段'b_id',然后按月份。这些记录的日期范围必须限制为自当前日期起的最后一年 我已经有了带ISNULL()的“b_id”字段的总计,如下所示:Sql server SQL-按月份和去年的字段值对记录进行计数和分组,sql-server,tsql,date,count,group-by,Sql Server,Tsql,Date,Count,Group By,我需要计算表“a”中记录的总数,其中“a”中的字段(比如“type”)有一个特定的值“v”。从所有这些a.type='v'的记录中,我需要对它们进行两次分组:首先按字段'b_id',然后按月份。这些记录的日期范围必须限制为自当前日期起的最后一年 我已经有了带ISNULL()的“b_id”字段的总计,如下所示: SELECT ISNULL( SELECT COUNT(*) FROM a WHERE a.type = 'v' AND b.b_id = a.b_id )
SELECT ISNULL(
SELECT COUNT(*)
FROM a
WHERE a.type = 'v'
AND b.b_id = a.b_id
), 0) AS b_totals
数据在表a中,并在表b中连接b_id'是表b的主键,可以在表a中找到(虽然它不是a的键的一部分)。a的键与我需要提取的数据无关,但为了简单起见,可以将其表示为“a_id”
我如何:
- 将这些记录限制在自当前日期起的过去十二个月内
- 取任意和所有b.id值的总和,并按月对其进行分类。这是每年b.id总数的补充。该日期作为标准日期/时间类型存储在表“a”的字段“date_Occurrent”中
b.id | b_totals | Nov. 2015 | Dec. 2015 | Jan. 2016 .... Oct. 2016
__________________________________________________________________
ID_1 1 0 0 0 1
ID_2 3 2 0 1 0
ID_3 5 1 1 3 0
编辑:我可能应该澄清,我正在计算表“a”中的记录,其中字段“f”具有特定的值“v”。从这些记录中,我需要按月份/日期对它们进行分组。我更新了ISNULL查询以使其更清晰,并更新了a和b的键。“发生日期”应该在表a中,而不是b中,这是我的错误/输入错误
如果有帮助,我可以从高层描述数据而不泄露任何敏感数据的最佳方式是:
- “b”是位置表,“b.b_id”是每个位置的id
- “a”是一个事件表。这些事件的位置在“a.b_id”中找到,并在“b.b_id”上加入。每个事件发生的日期在“a.b_发生的日期”中
- 我需要将事件类型限制为某个值。在本例中,类型为字段“type”。这是ISNULL SQL查询中的“where”子句,它按位置获取总计
- 从所有此类事件中,我需要计算过去一年中每个地点发生此类事件的次数。一旦我有了去年的总数,我需要按月统计
SELECT ISNULL(
SELECT COUNT(*)
FROM a
WHERE a.type = 'v'
AND b.b_id = a.b_id
AND a.date_occurred BETWEEN (DATEADD(yyyy, -1, GETDATE()) AND (GETDATE())
), 0) AS b_totals
现在需要用一个轴和月份来做这件事。为了从问题中提供的绝对最小细节中充分详细,我创建了以下两个示例表,其中包含一些数据:
CREATE TABLE Bexample
([ID] int)
;
INSERT INTO Bexample
([ID])
VALUES
(1),
(2),
(3),
(4),
(5),
(6),
(7),
(8),
(9)
;
CREATE TABLE Aexample
([ID] int, [B_PK] int, [SOME_DT] datetime)
;
INSERT INTO Aexample
([ID], [B_PK], [SOME_DT])
VALUES
(1, 1, '2015-01-01 00:00:00'),
(2, 2, '2015-02-01 00:00:00'),
(3, 3, '2015-03-01 00:00:00'),
(4, 4, '2015-04-01 00:00:00'),
(5, 5, '2015-05-01 00:00:00'),
(6, 6, '2015-06-01 00:00:00'),
(7, 7, '2015-07-01 00:00:00'),
(8, 8, '2015-08-01 00:00:00'),
(9, 9, '2015-09-01 00:00:00'),
(10, 1, '2015-10-01 00:00:00'),
(11, 2, '2015-11-01 00:00:00'),
(12, 3, '2015-12-01 00:00:00'),
(13, 1, '2016-01-01 00:00:00'),
(14, 2, '2016-02-01 00:00:00'),
(15, 3, '2016-03-01 00:00:00'),
(16, 4, '2016-04-01 00:00:00'),
(17, 5, '2016-05-01 00:00:00'),
(18, 6, '2016-06-01 00:00:00'),
(19, 7, '2016-07-01 00:00:00'),
(20, 8, '2016-08-01 00:00:00'),
(21, 9, '2016-09-01 00:00:00'),
(22, 1, '2016-10-01 00:00:00'),
(23, 2, '2016-11-01 00:00:00'),
(24, 3, '2016-12-01 00:00:00')
;
现在,使用这些表和数据,我可以生成如下结果表:
id Nov 2015 Dec 2015 Jan 2016 Feb 2016 Mar 2016 Apr 2016 May 2016 Jun 2016 Jul 2016 Aug 2016 Sep 2016 Oct 2016
1 0 0 1 0 0 0 0 0 0 0 0 1
2 1 0 0 1 0 0 0 0 0 0 0 0
3 0 1 0 0 1 0 0 0 0 0 0 0
4 0 0 0 0 0 1 0 0 0 0 0 0
5 0 0 0 0 0 0 1 0 0 0 0 0
6 0 0 0 0 0 0 0 1 0 0 0 0
7 0 0 0 0 0 0 0 0 1 0 0 0
8 0 0 0 0 0 0 0 0 0 1 0 0
9 0 0 0 0 0 0 0 0 0 0 1 0
SELECT id, [Nov 2015],[Dec 2015],[Jan 2016],[Feb 2016],[Mar 2016],[Apr 2016],[May 2016],[Jun 2016],[Jul 2016],[Aug 2016],[Sep 2016],[Oct 2016] FROM
(
select
format([mnth],'MMM yyyy') colname
, b.id
, a.b_pk
from #mylist
cross join bexample b
left join aexample a on #mylist.mnth = DATEADD(month, DATEDIFF(month,0,a.some_dt), 0)
and b.id = a.b_pk
) sourcedata
pivot
(
count([b_pk])
FOR [colname] IN ([Nov 2015],[Dec 2015],[Jan 2016],[Feb 2016],[Mar 2016],[Apr 2016],[May 2016],[Jun 2016],[Jul 2016],[Aug 2016],[Sep 2016],[Oct 2016])
) p
id name mnth
-- ------------- -------- -
1 Atlantic City 2016 Jan 1
1 Atlantic City 2016 Jul 1
2 Boston 2016 Aug 1
2 Boston 2016 Feb 1
3 Chicago 2016 Mar 1
3 Chicago 2016 Sep 1
4 Denver 2016 Apr 1
4 Denver 2016 Oct 1
5 Edgbaston 2015 Nov 1
5 Edgbaston 2016 May 1
5 Edgbaston 2016 Nov 1
6 Melbourne 2015 Dec 1
6 Melbourne 2016 Dec 1
6 Melbourne 2016 Jun 1
使用既需要“公共表表达式”(CTE)又需要“动态sql”的查询来生成该结果:
“动态SQL“是一个生成SQL并随后执行的查询。这是必需的,因为列名每月都在更改。因此,对于动态sql,我们声明了两个变量,它们将保存生成的sql。其中一个是存储列名称,在两个位置使用,另一个是保存完成的查询。注意:在开发解决方案时,您可以显示生成的SQL,而不是执行此操作(注意查询末尾执行附近的注释)
除了示例表和数据之外,我们还有一个“时间序列”要考虑12个月。这是“动态”的,因为它是从今天的日期开始计算的,我假设如果今天是2016年11月内的任何一天,“最后12个月”从2015年11月1日开始,到2016年10月31日结束(即12个完整月,没有部分月)
计算的核心是:DATEADD(month,-12, DATEADD(month, DATEDIFF(month,0,GETDATE()), 0) )
它首先使用DATEDIFF(month,0,GETDATE())
定位当前月份的第一天,然后从该日期再减去12个月。以此作为开始日期,“递归CTE”用于生成12行,在过去的12个完整月内,每个月生成一行
这12行的目的是确保当我们考虑实际表数据时,12列中没有空隙。这是通过在我们的查询中使用生成的12行作为“from表”来实现的,并且“A”表根据日期列[some_dt]的年/月与12个月的行保持连接
因此,我们生成12行,将示例数据与这些数据连接起来,用于生成数据“透视”所需的SQL。在这里,可以看到生成的sql的示例非常有用,如下所示:
id Nov 2015 Dec 2015 Jan 2016 Feb 2016 Mar 2016 Apr 2016 May 2016 Jun 2016 Jul 2016 Aug 2016 Sep 2016 Oct 2016
1 0 0 1 0 0 0 0 0 0 0 0 1
2 1 0 0 1 0 0 0 0 0 0 0 0
3 0 1 0 0 1 0 0 0 0 0 0 0
4 0 0 0 0 0 1 0 0 0 0 0 0
5 0 0 0 0 0 0 1 0 0 0 0 0
6 0 0 0 0 0 0 0 1 0 0 0 0
7 0 0 0 0 0 0 0 0 1 0 0 0
8 0 0 0 0 0 0 0 0 0 1 0 0
9 0 0 0 0 0 0 0 0 0 0 1 0
SELECT id, [Nov 2015],[Dec 2015],[Jan 2016],[Feb 2016],[Mar 2016],[Apr 2016],[May 2016],[Jun 2016],[Jul 2016],[Aug 2016],[Sep 2016],[Oct 2016] FROM
(
select
format([mnth],'MMM yyyy') colname
, b.id
, a.b_pk
from #mylist
cross join bexample b
left join aexample a on #mylist.mnth = DATEADD(month, DATEDIFF(month,0,a.some_dt), 0)
and b.id = a.b_pk
) sourcedata
pivot
(
count([b_pk])
FOR [colname] IN ([Nov 2015],[Dec 2015],[Jan 2016],[Feb 2016],[Mar 2016],[Apr 2016],[May 2016],[Jun 2016],[Jul 2016],[Aug 2016],[Sep 2016],[Oct 2016])
) p
id name mnth
-- ------------- -------- -
1 Atlantic City 2016 Jan 1
1 Atlantic City 2016 Jul 1
2 Boston 2016 Aug 1
2 Boston 2016 Feb 1
3 Chicago 2016 Mar 1
3 Chicago 2016 Sep 1
4 Denver 2016 Apr 1
4 Denver 2016 Oct 1
5 Edgbaston 2015 Nov 1
5 Edgbaston 2016 May 1
5 Edgbaston 2016 Nov 1
6 Melbourne 2015 Dec 1
6 Melbourne 2016 Dec 1
6 Melbourne 2016 Jun 1
因此,希望您可以在生成的SQL代码中看到,动态创建的12行变为12列。请注意,因为我们正在执行“动态sql”,所以作为CTE生成的12行需要存储为“临时表”(#mylist)
生成并执行该SQL的查询如下所示
DECLARE @cols AS VARCHAR(MAX)
DECLARE @query AS VARCHAR(MAX)
;with mylist as (
select DATEADD(month,-12, DATEADD(month, DATEDIFF(month,0,GETDATE()), 0) ) as [mnth]
union all
select DATEADD(month,1,[mnth])
from mylist
where [mnth] < DATEADD(month,-1, DATEADD(month, DATEDIFF(month,0,GETDATE()), 0) )
)
select [mnth]
into #mylist
from mylist
SELECT @cols = STUFF((SELECT ',' + QUOTENAME(format([mnth],'MMM yyyy'))
FROM #mylist
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET @query = 'SELECT id, ' + @cols + ' FROM
(
select
format([mnth],''MMM yyyy'') colname
, b.id
, a.b_pk
from #mylist
cross join bexample b
left join aexample a on #mylist.mnth = DATEADD(month, DATEDIFF(month,0,a.some_dt), 0)
and b.id = a.b_pk
) sourcedata
pivot
(
count([b_pk])
FOR [colname] IN (' + @cols + ')
) p '
--select @query -- use select to inspect the generated sql
execute(@query) -- once satisfied that sql is OK, use execute
drop table #mylist
将@cols声明为VARCHAR(MAX)
将@query声明为VARCHAR(MAX)
;以mylist为例(
选择DATEADD(month,-12,DATEADD(month,DATEDIFF(month,0,GETDATE()),0))作为[mnth]
联合所有
选择日期添加(月,1,[mnth])
来自mylist
其中[mnth]