将两个查询组合在一起以形成月份计数

时间:2016-11-29 11:33:20

标签: sql-server tsql

我目前有以下查询:

select 
    DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0) as MonthYear, count(*) as Count
from 
    visit
where 
    StartDate >= DATEADD(year,-1,GETDATE())
    and clientid = 142
group by  
    DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0)

返回以下结果:

+-------------------------+----+
| 2015-12-01 00:00:00.000 |  1 |
| 2016-02-01 00:00:00.000 | 13 |
| 2016-03-01 00:00:00.000 |  1 |
| 2016-04-01 00:00:00.000 | 22 |
| 2016-05-01 00:00:00.000 | 22 |
| 2016-06-01 00:00:00.000 | 25 |
| 2016-07-01 00:00:00.000 | 36 |
+-------------------------+----+

我还有以下查询:

SELECT TOP (12) 
    DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - test, 0) as        MonthYear
FROM
    test

返回以下结果:

+-------------------------+
| 2016-10-01 00:00:00.000 |
| 2016-09-01 00:00:00.000 |
| 2016-08-01 00:00:00.000 |
| 2016-07-01 00:00:00.000 |
| 2016-06-01 00:00:00.000 |
| 2016-05-01 00:00:00.000 |
| 2016-04-01 00:00:00.000 |
| 2016-03-01 00:00:00.000 |
| 2016-02-01 00:00:00.000 |
| 2016-01-01 00:00:00.000 |
| 2015-12-01 00:00:00.000 |
| 2015-11-01 00:00:00.000 |
+-------------------------+

我需要做的是将两个查询合并为一个查询,并针对原始查询中缺少的月份显示 0

有人可以帮忙吗?

10 个答案:

答案 0 :(得分:3)

传统上这是通过日历表(例如this msdn answer)解决的,但在您的情况下,派生表和外连接应该有效:

select DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0) as MonthYear,count(*) as Count
    from (SELECT TOP (12) DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-test, 0) as MonthYear
  from test) cal LEFT OUTER JOIN visit on (cal.MonthYear = visit.MonthYear)
    where StartDate >= DATEADD(year,-1,GETDATE())
    and clientid = 142
    group by  DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0)

答案 1 :(得分:1)

此方法使用CROSS APPLY和VALUES创建主月份列表。

如果可以的话,我建议用变量替换GETDATE()。

CREATE TABLE #Visit(StartDate DATE,clientid int)

INSERT INTO #Visit VALUES
('20160304',142)
,('20160305',142)
,('20160508',142)
,('20160612',142)
,('20160617',142)

SELECT 
    T.Mnth
    ,COUNT(V.StartDate) AS [Count]
FROM 
     (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) c (test)
        CROSS APPLY (SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - c.test, 0) Mnth) T 
        LEFT JOIN #Visit V
            ON
            DATEDIFF(MM,V.startdate,T.Mnth) = 0
            AND StartDate >= DATEADD(year,-1,GETDATE())
            AND clientid = 142
GROUP BY
    T.Mnth
ORDER BY 
    T.Mnth DESC 

答案 2 :(得分:0)

LEFT JOINISNULL的几个子查询应该这样做:

SELECT sub1.MonthYear,
       ISNULL(sub2.Count, 0)
FROM
(SELECT TOP (12) DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - test, 0) AS MonthYear
 FROM test t) sub1
LEFT JOIN
(SELECT DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0) as MonthYear,
        COUNT(*) AS Count
 FROM visit
 WHERE StartDate >= DATEADD(year,-1,GETDATE())
   AND clientid = 142
 GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0)) sub2
ON sub1.MonthYear = sub2.MonthYear

答案 3 :(得分:0)

如果您要进行大量的差距和岛屿计算以及基于日期的查询而没有差距,那么我无法强调日历表会让您的生活变得多么简单。

基本上,您只需要一张类似于:

的表格

<强>日历

    Day INT,
    Month INT, 
    Year INT, 
    StartOfDay DATETIME,
    EndOfDay DATETIME,
    DayOfWeek INT, 
    WeekInyear INT, 
    IsWeekDay BIT

选择日期范围。我选择了本世纪和上世纪,因为它符合我的商业模式需求。接下来,只需运行一次循环函数并填充表格,最后应用适当的索引并根据需要使用。

DECLARE @BeginDate DATETIME ='01/01/2015'
DECLARE @EndDate DATETIME   ='01/01/2017' 

DECLARE @MONTHS TABLE(Month INT,Year INT)

WHILE @BeginDate<=@EndDate BEGIN
    INSERT @Months SELECT DATEPART(MONTH,@BeginDate),DATEPART(YEAR,@BeginDate)
    SET @BeginDate=DATEADD(MONTH,1,@BeginDate)
END

SELECT 
    Calendar.Month,
    Calendar.Year,
    COUNT(visit.userID) 
FROM 
    @Months Calendar
    LEFT JOIN visit ON visit.year=Calendar.Year AND visit.Month=Calendar.Month
GROUP BY
    Calendar.Month,
    Calendar.Year

答案 4 :(得分:0)

我经常使用TVF创建动态日期/时间范围。这个udf比递归cte方法更快(特别是在较大的集合上)。此外,您还可以定义日期范围,日期部分和增量。我应该注意,一个计数表也可以做到这一点。

-- Generate Some Sample Data
Declare @Visit table (StartDate date,Cnt int)
Insert Into @Visit values
('2015-12-01 00:00:00.000', 1),
('2016-02-01 00:00:00.000',13),
('2016-03-01 00:00:00.000', 1),
('2016-04-01 00:00:00.000',22),
('2016-05-01 00:00:00.000',22),
('2016-06-01 00:00:00.000',25),
('2016-07-01 00:00:00.000',36)

-- Set Key Dates
Declare @DateR2 date = '2016-10-01'
Declare @DateR1 date = DateAdd(YY,-1,@DateR2)

-- Execute Query
Select DateR1
      ,Cnt    = isnull(sum(Cnt),0)
 From  (Select DateR1=RetVal,DateR2=DateAdd(MM,1,RetVal)-1 From [dbo].[udf-Range-Date](@DateR1,@DateR2,'MM',1)) A
 Left  Join @Visit B on B.StartDate Between A.DateR1 and A.DateR2
 Group By DateR1
 Order by DateR1

返回

Period      Cnt
2015-10-01  0
2015-11-01  0
2015-12-01  1
2016-01-01  0
2016-02-01  13
2016-03-01  1
2016-04-01  22
2016-05-01  22
2016-06-01  25
2016-07-01  36
2016-08-01  0
2016-09-01  0
2016-10-01  0

UDF(如果需要)

CREATE FUNCTION [dbo].[udf-Range-Date] (@R1 datetime,@R2 datetime,@Part varchar(10),@Incr int)
Returns Table
Return (
    with cte0(M)   As (Select 1+Case @Part When 'YY' then DateDiff(YY,@R1,@R2)/@Incr When 'QQ' then DateDiff(QQ,@R1,@R2)/@Incr When 'MM' then DateDiff(MM,@R1,@R2)/@Incr When 'WK' then DateDiff(WK,@R1,@R2)/@Incr When 'DD' then DateDiff(DD,@R1,@R2)/@Incr When 'HH' then DateDiff(HH,@R1,@R2)/@Incr When 'MI' then DateDiff(MI,@R1,@R2)/@Incr When 'SS' then DateDiff(SS,@R1,@R2)/@Incr End),
         cte1(N)   As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
         cte2(N)   As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
         cte3(N,D) As (Select 0,@R1 Union All Select N,Case @Part When 'YY' then DateAdd(YY, N*@Incr, @R1) When 'QQ' then DateAdd(QQ, N*@Incr, @R1) When 'MM' then DateAdd(MM, N*@Incr, @R1) When 'WK' then DateAdd(WK, N*@Incr, @R1) When 'DD' then DateAdd(DD, N*@Incr, @R1) When 'HH' then DateAdd(HH, N*@Incr, @R1) When 'MI' then DateAdd(MI, N*@Incr, @R1) When 'SS' then DateAdd(SS, N*@Incr, @R1) End From cte2 )

    Select RetSeq = N+1
          ,RetVal = D 
     From  cte3,cte0 
     Where D<=@R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1) 
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1) 
*/

答案 5 :(得分:0)

;with cteCalendar as 
(
  SELECT TOP (12) 
      DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - test, 0) as        MonthYear
  FROM
      test
),
cteVisits as
(
  select 
      DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0) as MonthYear, count(*) as Count
  from 
      visit
  where 
      StartDate >= DATEADD(year,-1,GETDATE())
      and clientid = 142
  group by  
      DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0)
)
SELECT
  c.MonthYear,
  ISNULL(v.Count, 0) as VisitCount
FROM cteCalendar c
LEFT JOIN cteVisits v
ON v.MonthYear = c.MonthYear
ORDER BY c.MonthYear

答案 6 :(得分:0)

所以有很多关于如何创建MONTH表的答案和好的,然后请加入您的访问以获得答案。但我只是想告诉你,如果访问表中每个月至少有一个日期,条件聚合可以如何帮助你。注意,我不是指您要过滤的客户端的1条记录,而是指任何/所有客户端的记录。换句话说,每个月至少会有一个客户?如果是这样,你可以做条件聚合来得到这样的答案:

select 
    DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0) as MonthYear
    ,count(CASE WHEN clientid = 142 THEN clientid END) as Count
from 
    visit
where 
    StartDate >= DATEADD(year,-1,GETDATE())
group by  
    DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0)

如果没有,那么你需要一个日期表@pacreely向你展示一个很好的方法。

答案 7 :(得分:0)

您可以按如下方式格式化查询

;with CTE AS (select 
    DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0) as MonthYear, count(*) as Count
from 
    visit
where 
    StartDate >= DATEADD(year,-1,GETDATE())
    and clientid = 142
group by  
    DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0)

)
, cte2 AS(
SELECT TOP (12) 
    DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - test, 0) as        MonthYear
FROM
    test )

    SELECT ISNULL(cte.MonthYear, cte2.MonthYear) AS MonthYear , ISNULL(cte.Count, 0) as Count
    from cte FULL OUTER join cte2 on cte.MonthYear = cte2.MonthYear

答案 8 :(得分:-1)

declare @Dates table(date datetime, count int)

select 
    DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0) as MonthYear, count(*) as Count
    into @Dates
from 
    visit
where 
    StartDate >= DATEADD(year,-1,GETDATE())
    and clientid = 142
group by  
    DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0)

    SELECT TOP (12) 
    into @Dates
    DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - test, 0) as        MonthYear,0
FROM
    test

    select * from @Dates

答案 9 :(得分:-1)

在假设“测试”表是一个表,列出了至少过去一年的每个月的第一个月,并且可能还包括未来的日期,这是我的建议:

With VisitList as
(SELECT 
DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0) as MonthYear, count(*) as VisitCount
FROM visit
WHERE
StartDate >= DATEADD(year,-1,GETDATE())
and clientid = 142
GROUP BY  
DATEADD(MONTH, DATEDIFF(MONTH, 0, StartDate), 0)

UNION

SELECT testDate, 0 AS VisitCount 
FROM test
WHERE testDate BETWEEN DATEADD(YEAR, -1, GETDATE()) AND GETDATE())

SELECT MonthYear, Sum(VisitCount) AS VisitCount FROM VisitList
GROUP BY MonthYear
ORDER BY MonthYear DESC