sql - 如何获取按月和年分组的数据(包括没有数据的时期)?

标签 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。

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

如有任何帮助,我们将不胜感激。

最佳答案

如果您使用的是SQL Server 2005 或更高版本,您可以使用公用表表达式(CTE) 来获得所需的结果。下面的示例显示了如何获取问题中描述的结果。

Click here to view the demo in SQL Fiddle.

描述:

  • Create 和 insert 语句创建表并填充一些示例数据。我根据问题中提供的查询创建了表格。
  • WITH 子句中的语句正在执行递归表达式。在本例中,UNION ALL 上方的 SELECT 会获取表 dbo.countproject
  • 中可用的最小和最大日期
  • 获取最小日期后,UNION ALL 之后的第二个 SELECT 语句将以 1 个月的间隔递增日期,直到递归表达式达到表中可用的最大日期。
  • 递归 CTE 已生成所有可能的可用日期。此输出可在名为 alltransactions 的表中找到。
  • 我们必须使用 LEFT OUTER JOIN 将此 CTE 输出 alltransactions 与实际表 countproject 连接起来,因为我们想要显示所有年份和月份即使没有交易。
  • 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

关于sql - 如何获取按月和年分组的数据(包括没有数据的时期)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10389915/

相关文章:

sql - 选择锯齿模式中的最大值(局部最大值)

sql - 如何更改 hierarchyid 中子项和子项的父 ID

mySQL:当meta_key不存在时将其插入表中

mysql - 如果未在 NOT NULL 列上指定值,如何使 MySQL 产生错误?

javascript - 如何检查值是否未定义,如果是,则将其设置为 null

WPF ShowDialog 在第二次调用时立即返回 null

sql - 更新表中具有相同值的列

mysql - 选择特定字段的值超过同一字段最大值一半的行

MySQL 使用 select max (`Column` )+1 插入可能为空的结果

html - 在CSS中设置选择和输入元素的宽度