Database 按年份和月份计数记录,包括零计数

Database 按年份和月份计数记录,包括零计数,database,join,count,sql-server-ce,webmatrix,Database,Join,Count,Sql Server Ce,Webmatrix,我使用的是SQL Server Compact Edition服务器,我想计算在一定日期范围内每月与某个教程相对应的评论数,包括计数为零的月份。我知道我需要将一个“日历”表加入到我的表中,以说明缺少的月份,但我需要帮助正确实现这一点 我有一个表格,里面有来自不同教程的所有评论。此表称为注释,我需要的列是[Tutorial](nvarchar)和[DateAdded](DateTime) 我有一个日历表,它有一个年和月列,如下所示: Year | Month -----+------ 2000 |

我使用的是SQL Server Compact Edition服务器,我想计算在一定日期范围内每月与某个教程相对应的评论数,包括计数为零的月份。我知道我需要将一个“日历”表加入到我的表中,以说明缺少的月份,但我需要帮助正确实现这一点

我有一个表格,里面有来自不同教程的所有评论。此表称为注释,我需要的列是
[Tutorial]
nvarchar
)和
[DateAdded]
DateTime

我有一个
日历
表,它有一个年和月列,如下所示:

Year | Month
-----+------
2000 | 01
2000 | 02
.    | .
.    | .
.    | .
2099 | 12
如果我在寻找过去一年(截至2014年2月14日)的每月“样本”评论数,那么理想的输出是:

Tutorial | Year | Month | Count
---------+------+-------+------
 sample  | 2013 |  09   |  7
 sample  | 2013 |  10   |  1
 sample  | 2013 |  11   |  6
 sample  | 2013 |  12   |  0
 sample  | 2014 |  01   |  0
 sample  | 2014 |  02   |  2
我能够找出如何执行以下查询,但我需要在没有注释的月份也返回0

SELECT 
     Tutorial, 
     datepart(year, DateAdded) AS Year, 
     datepart(month, DateAdded) AS Month, 
     COUNT(*) AS Count From Comments 
WHERE 
     DateAdded > DATEADD(year,-1,GETDATE())  
       AND 
     Tutorial='sample' 
GROUP BY 
     Tutorial, 
     datepart(year, DateAdded), 
     datepart(month, DateAdded)
使用上面的示例数据输出

Tutorial | Year | Month | Count
---------+------+-------+------
 sample  | 2013 |  09   |  7
 sample  | 2013 |  10   |  1
 sample  | 2013 |  11   |  6
 sample  | 2014 |  02   |  2
我知道我需要连接表,但我似乎无法确定要使用哪个连接或如何正确实现它。请记住,这是针对SQL Server CE的,因此并非所有来自SQL Server的命令都可以使用


非常感谢

下面是一个独立的脚本,您可以使用它进行尝试,而不必在生产环境中接触任何真实的数据库对象。代码的下三分之一包含您正在寻找的连接的帮助

SQLServerCE将允许您编写存储过程,而存储过程又可以用作报表的源。存储的进程很好,因为它们可以接受输入参数,这是执行报告的理想选择

-- create dummy Comments table for prototyping
create table #Comments (
    ID int identity(1,1) not null,
    Tutorial nvarchar(50) not null,
    DateAdded datetime not null,
    primary key clustered(DateAdded,ID,Tutorial)
);

-- populate dummy Comments table
declare @startDate datetime = '2000-01-01';
declare @endDate datetime = '2014-02-14';
declare @numTxns int = 5000;

set nocount on;

declare @numDays int = cast(@endDate as int) - cast(@startDate as int) + 1;
declare @i int = 1;
declare @j int = @i + @numTxns;
declare @rnd float;
while @i <= @j
begin
    set @rnd = RAND();
    insert into #Comments (Tutorial,DateAdded)
    select 
    -- random tutorial titles
        coalesce (
            case when @rnd < .25 then 'foo' else null end,
            case when @rnd between .5 and .75 then 'baz' else null end, 
            case when @rnd > .75 then 'qux' else null end,
            'bar'
        ) as Tutorial,
    -- random dates between @startDate and @endDate
        cast(cast(rand() * @numDays + @startDate as int) as datetime) as DateAdded
    set @i = @i + 1
end;

-- try deleting some months to see what happens
delete from #Comments
where DateAdded between '2013-11-01' and '2013-11-30'
    or DateAdded between '2014-01-01' and '2014-01-31';

set nocount off;
go

-- ### following could easily be rewritten as a stored procedure

-- stored procedure parameters
declare @startDate datetime = '2000-01-01';
declare @endDate datetime = '2014-03-31';
-- pick only one option below
--declare @Tutorial nvarchar(50) = 'foo'; -- this only gets data for Tutorials called 'foo'
declare @Tutorial nvarchar(50) = 'all'; -- this gets data for all tutorials

-- begin stored procedure code
set nocount on;

-- this temp table is an alternative to 
-- creating ***and maintaining*** a table full of dates,
-- months, etc., and cluttering up your database
-- in production, it will automatically delete itself
-- once the user has completed running the report.
create table #dates (
    DateAdded datetime not null,
    YearAdded int null,
    MonthAdded int null,
    primary key clustered (DateAdded)
);

-- now we put dates into #dates table
-- based on the parameters supplied by 
-- the user running the report
declare @date datetime = @startDate;
while @date <= @endDate
begin
    insert into #dates
    select @date, YEAR(@date), MONTH(@date);

    set @date = @date + 1;
end;

-- ## Why put every day of the month in this table?
-- ## I asked for a monthy report, not daily!
-- Yes, but looping through dates is easier, simply add 1 for the next date.
-- You can always build a monthly summary table later if you'd like.
-- This *is* kind of a brute-force solution, but easy to write.
-- More answers to this question in the code below, where they'll make more sense.

set nocount off;

-- now we return the data to the user
-- any month with no Tutorials will still show up in the report
-- but the counts will show as zero
select YearAdded, MonthAdded, SUM(Count_From_Comments) as Count_From_Comments,
    SUM(foo) as Count_Foo, SUM(bar) as Count_Bar, 
    SUM(baz) as Count_Baz, SUM(qux) as Count_Qux
from (
-- ## you can reuse the following code for a detail report by day
-- ## another answer to 'Why not by month?' from above
-- start daily report code
    select t1.DateAdded, t1.YearAdded, t1.MonthAdded, t2.Tutorial, 
            coalesce(Count_From_Comments,0) as Count_From_Comments, 
            case when t2.Tutorial = 'foo' then 1 else 0 end as foo,
            case when t2.Tutorial = 'bar' then 1 else 0 end as bar,
            case when t2.Tutorial = 'baz' then 1 else 0 end as baz,
            case when t2.Tutorial = 'qux' then 1 else 0 end as qux
    from #dates as t1 -- no where clause needed because #dates only contains the ones we want
    left join ( -- left join here so that we get all dates, not just ones in #Comments
        select *, 1 AS Count_From_Comments
        from #Comments
        where @Tutorial in (Tutorial,'all')
    ) as t2
        on t1.DateAdded = t2.DateAdded -- ## join on one field instead of two, another answer to 'Why not by month?' from above
-- end daily report code
) as qDetail
group by YearAdded, MonthAdded
order by YearAdded, MonthAdded

-- end stored procedure code
go

-- ## Not required in production code,
-- ## but handy when testing this script.
drop table #dates;

-- #### Since this will be a real table in production
-- #### we definitely only want this for testing!
drop table #Comments;

go
——为原型创建虚拟注释表
创建表#注释(
ID int标识(1,1)不为空,
教程nvarchar(50)不为空,
DateAdded datetime不为空,
主键群集(已添加日期、ID、教程)
);
--填充虚拟注释表
声明@startDate datetime='2000-01-01';
声明@endDate datetime='2014-02-14';
声明@numTxns int=5000;
不计数;
声明@numDays int=cast(@endDate为int)-cast(@startDate为int)+1;
声明@i int=1;
声明@j int=@i+@numTxns;
宣布@rnd浮动;
而@i.75然后'qux'则为空端,
“酒吧”
)作为教程,
--@startDate和@endDate之间的随机日期
强制转换(强制转换(rand()*@numDays+@startDate为int)为datetime)为DateAdded
设置@i=@i+1
结束;
--尝试删除几个月,看看会发生什么
从#注释中删除
其中日期添加在“2013-11-01”和“2013-11-30”之间
或在“2014-01-01”和“2014-01-31”之间添加的日期;
不计数;
去
--######以下内容可以很容易地重写为存储过程
--存储过程参数
声明@startDate datetime='2000-01-01';
声明@endDate datetime='2014-03-31';
--只选择下面的一个选项
--声明@Tutorial nvarchar(50)=“foo”;--这只获取名为“foo”的教程的数据
声明@Tutorial nvarchar(50)=“全部”;--这将获取所有教程的数据
--开始存储过程代码
不计数;
--此临时表是一个替代表
--创建***并维护***一个充满日期的表格,
--几个月等等,把你的数据库弄得乱七八糟
--在生产中,它将自动删除自身
--一旦用户完成报告的运行。
创建表#日期(
DateAdded datetime不为空,
YearAdded int null,
蒙塔德整数为空,
主键群集(已添加日期)
);
--现在我们将日期放入#dates表中
--根据制造商提供的参数
--运行报表的用户
声明@date datetime=@startDate;

而@date如果你有一个
日历
表格,上面有
,你应该尝试以下方法

SELECT t2.Tutorial, t1.[Month], t1.[Year], COALESCE(t2.Number, 0) AS Result
  FROM Calendar AS t1 LEFT JOIN (
    SELECT 
      Tutorial, 
      CONVERT(NCHAR(6), DateAdded, 112) AS tutDate,
      COUNT(*) AS Count From Comments 
    WHERE 
      DateAdded > DATEADD(year,-1,GETDATE())  
      AND 
      Tutorial='sample' 
    GROUP BY 
      Tutorial, 
      CONVERT(NCHAR(6), [Order Date], 112)
  ) AS t2
  ON (t1.[Year] + t1.[Month]) = t2.tutDate
  ORDER BY t1.[Year] + t1.[Month]

这些都不适用于SQL CE
SELECT t2.Tutorial, t1.[Month], t1.[Year], COALESCE(t2.Number, 0) AS Result
  FROM Calendar AS t1 LEFT JOIN (
    SELECT 
      Tutorial, 
      CONVERT(NCHAR(6), DateAdded, 112) AS tutDate,
      COUNT(*) AS Count From Comments 
    WHERE 
      DateAdded > DATEADD(year,-1,GETDATE())  
      AND 
      Tutorial='sample' 
    GROUP BY 
      Tutorial, 
      CONVERT(NCHAR(6), [Order Date], 112)
  ) AS t2
  ON (t1.[Year] + t1.[Month]) = t2.tutDate
  ORDER BY t1.[Year] + t1.[Month]