如何获取按月和年分组的数据,包括没有数据的时间段?

时间:2012-04-30 20:08:13

标签: sql select null count

相对较新的SQL,我对这个小问题感到困惑。这似乎并不是很难做到,但我似乎无法弄明白。

我正在尝试从表中获取交易数量,但我似乎无法让sql让我显示所有月份,而不仅仅是交易发生的月份和年份。

以下是查询:

SELECT      YEAR(dbo.countproject.trans_date)   AS [TransYear]
        ,   MONTH (dbo.countproject.trans_date) AS [TransMonth]
        ,   COUNT(Id)                           AS TransNum 
FROM        dbo.countproject
WHERE       dbo.countproject.make_name  = 'Honda'
AND         dbo.countproject.model_name = 'Civic'
AND         dbo.countproject.type       = 'Sale'
AND         dbo.countproject.trans_type LIKE '%%EU'
AND         dbo.countproject.mfr        = '2000'
GROUP BY    YEAR(dbo.countproject.trans_date)
        ,   MONTH(dbo.countproject.trans_date)
ORDER BY    YEAR(dbo.countproject.trans_date)

查询返回以下结果集:

| TransYear | TransMonth | TransNum |
|-----------|------------|----------|
|  2004     |     1      |     5    |
|  2004     |     3      |     1    |
|  2005     |     4      |     2    |

等等....

即使值为NULL,我也试图让它显示所有月份和年份。

我尝试创建一个新表,将年份和月份作为列以使其以某种方式加入,但我迷失了。

任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:3)

如果您使用的是SQL Server 2005 or above,则可以使用Common Table Expressions (CTE)来获得所需的结果。下面的示例显示了如何按照问题中的描述获取结果。

Click here to view the demo in SQL Fiddle.

描述

  • 创建和插入语句创建表并填充一些示例数据。我已根据问题中提供的查询创建了表格。
  • WITH子句中的语句正在执行递归表达式。在这种情况下,SELECT上方的UNION ALL会获取表格中可用的最小和最大日期 dbo.countproject
  • 获取最小日期后,UNION ALL之后的第二个SELECT语句以1个月的间隔递增日期,直到递归表达式达到表中可用的最大日期。
  • 递归CTE已生成所有可用日期。此输出位于名为 alltransactions 的表中。
  • 我们必须使用alltransactions将此CTE输出countproject与实际表格LEFT OUTER JOIN相关联,因为我们希望显示所有年份和月份,即使没有交易。
  • 表格alltransactionscountproject已加入日期的部分。然后,查询在 WHERE 子句中应用必要的过滤器,然后按年份和月份对数据进行分组,然后按年份和月份进行排序。
  • 您可以从示例数据中注意到表格中的最早日期为2004-07-01,最新日期为2005-12-01。因此,产出显示从2004年/月07年到2005年/ 12月。

希望有所帮助。

脚本

CREATE TABLE dbo.countproject
(   
        id          INT         NOT NULL IDENTITY
    ,   trans_date  DATETIME    NOT NULL
    ,   make_name   VARCHAR(20) NOT NULL
    ,   model_name  VARCHAR(20) NOT NULL
    ,   type        VARCHAR(20) NOT NULL
    ,   trans_type  VARCHAR(20) NOT NULL
    ,   mfr         INT         NOT NULL
);

INSERT INTO dbo.countproject (trans_date, make_name, model_name, type, trans_type, mfr) VALUES
    ('1900-01-01', 'Honda',     'Civic',    'Sale', 'EU', 2000),
    ('1900-01-01', 'Toyota',    'Corolla',  'Sale', 'EU', 2000),
    ('2004-07-01', 'Nissan',    'Altima',   'Sale', 'EU', 2000),
    ('2005-12-01', 'Toyota',    'Camry',    'Sale', 'EU', 2000),
    ('2004-04-01', 'Ford',      'Focus',    'Sale', 'EU', 2000),
    ('2005-08-01', 'Honda',     'Civic',    'Sale', 'EU', 2000),
    ('2005-11-01', 'Toyota',    'Camry',    'Sale', 'EU', 2000),
    ('2004-08-01', 'Toyota',    'Corolla',  'Sale', 'EU', 2000),
    ('2005-12-01', 'Honda',     'Civic',    'Sale', 'EU', 2000),
    ('2004-07-01', 'Honda',     'Civic',    'Sale', 'EU', 2000),
    ('2004-11-01', 'Honda',     'Civic',    'Sale', 'EU', 2000),
    ('2005-08-01', 'Honda',     'Civic',    'Sale', 'EU', 2000);


;WITH alltransactions
AS
(
    SELECT      MIN(trans_date) AS continuousdate
            ,   MAX(trans_date) AS maximumdate
    FROM        dbo.countproject
    WHERE           trans_date <> '1900-01-01'
    UNION ALL 
    SELECT      DATEADD(MONTH, 1, continuousdate) AS continuousdate
            ,   maximumdate
    FROM        alltransactions
    WHERE       DATEADD(MONTH, 1, continuousdate) <= maximumdate
)
SELECT          YEAR(at.continuousdate)     AS [Year]
            ,   MONTH(at.continuousdate)    AS [Month]

            ,   COUNT(cp.trans_date)        AS [Count]
FROM            alltransactions at
LEFT OUTER JOIN countproject    cp
ON              YEAR(at.continuousdate)     = YEAR(cp.trans_date)
AND             MONTH(at.continuousdate)    = MONTH(cp.trans_date)
AND             cp.make_name                = 'Honda'
and             cp.model_name               = 'Civic'
and             cp.type                     = 'Sale'
and             cp.trans_type               LIKE '%EU'
and             cp.mfr                      = '2000'
GROUP BY        YEAR(at.continuousdate)
            ,   MONTH(at.continuousdate)
ORDER BY        [Year]
            ,   [Month];

输出

Year   Month  Count
-----  ------ -----
2004      4     0
2004      5     0
2004      6     0
2004      7     1
2004      8     0
2004      9     0
2004     10     0
2004     11     1
2004     12     0
2005      1     0
2005      2     0
2005      3     0
2005      4     1
2005      5     0
2005      6     0
2005      7     0
2005      8     2
2005      9     0
2005     10     0
2005     11     0
2005     12     1

答案 1 :(得分:0)

你必须使用LEFT或RIGHT OUTER JOIN!

以下是一个简单的示例:http://www.w3schools.com/sql/sql_join_left.asp

你应该自己完成它。

问候

答案 2 :(得分:0)

唉,SQL语句只能返回表中的数据。如果您想要所有月份,您需要一张表格,其中包含您感兴趣的年/月组合,或者最好是包含所有日期和相关信息的日历表。

使用日历表,您的查询有一个from子句,如下所示:

from 
(
   select distinct year(date) as yr, month(date) as mon
   from calendar c
   where date between <earliest> and <latest>
) c 
left outer join CountTable ct
  on c.yr = year(ct.trans_date) 
  and c.mon = month(ct.trans_date)