每月约会,12月失踪?

时间:2017-03-16 05:08:18

标签: mysql sqlyog

SELECT month(dateofappointment), COUNT(*) 'NumberOfAppointments'
    FROM appointment 
    WHERE YEAR(dateofappointment) = '2016'  
    GROUP BY  MONTH(dateofappointment) 

这显示了我所有月份,但12月不存在,因为那一年没有任何约会。我如何将12月显示为0?

1 个答案:

答案 0 :(得分:0)

要解决这些类型的查询,通常有助于将它们表达为一系列要求,这样可以更容易解决。

如果结果未按预期显示,请在识别时根据新要求更新您的需求声明,然后重试:

我现在看到你有两个要求:

  1. 2016年每个月的单行返回
  2. 每行显示相应月份的约会计数
  3. 好的,所以这很详细,但是你看到你的查询中缺少的是一个声明,它定义了2016年每个月的 1行' 1行。因此,您需要首先手动或通过递归来构建该记录集。

      

    MySQL目前不支持递归公用表表达式,这在许多其他RDBMS中都是一个微不足道的概念

    但如果MySQL不支持递归,我们的选择是什么?以下是SO的其他一些尝试:

    这可能听起来有点像黑客攻击,但你可以使用数据库中任何超过12行且具有自动递增字段的表,哦,并且从1开始(或以下)播种。忘记这是对还是错,它会起作用:

    SELECT Id 
    FROM LogEvent -- An arbitrary table that I know has records starting from 1
    WHERE Id BETWEEN 1 AND 12
    

    所以这很麻烦,但我们可以实现一个行计数功能,这样我们就可以使用任何包含12行或更多行的表,无论id还是种子,都可以从MySQL get row number on select - Answer by Mike Cialowicz偷走它

    SET @rank=0;
    SELECT @rank:=@rank+1 AS rank
    FROM orders
    WHERE rank <= 12
    

    现在我们可以将此结果集中缺失的行合并到原始查询中,或者使用连接运算符。使用union的第一个解决方案。

      

    通常使用UNION ALL将缺少的行注入记录集,因为它将预期结果查询与异常或默认结果分开。有时这种语法可以更容易地解释预期的操作

    SET @rank = 0;
    
    SELECT month(dateofappointment) as Month, COUNT(*) 'NumberOfAppointments'
    FROM appointment 
    WHERE YEAR(dateofappointment) = '2016'  
    GROUP BY  MONTH(dateofappointment) 
    
    UNION ALL
    
    SELECT rank, 0
    FROM (
        SELECT @rank:=@rank+1 AS rank
        FROM rows
        WHERE @rank < 12
    ) months
    WHERE NOT EXISTS (SELECT dateofappointment 
                      FROM appointment
                      WHERE YEAR(dateofappointment) = '2016' AND MONTH(dateofappointment) = months.rank)
    ORDER BY Month
    

    但这会导致丑陋的查询。您也可以使用左连接加入有关约会计数的月份查询,但此处的意图更难识别。

    SET @rank = 0;
    
    SELECT months.rank, COUNT(appointment.dateofappointment) 
    FROM (
        SELECT @rank:=@rank+1 AS rank
        FROM rows
        WHERE @rank < 12
    ) months
    LEFT OUTER JOIN appointment ON months.rank = Month(appointment.dateofappointment) AND YEAR(dateofappointment) = '2016'
    GROUP BY months.rank
    

    I have saved these queries into a SqlFiddle so you can see the resultshttp://sqlfiddle.com/#!9/99d485/4

      

    正如我上面所指出的,这在MS SQL和Oracle RDBMS中是微不足道的,我们可以通过递归公用表表达式(CTE)动态生成值序列。对于家中的玩家来说,这是MS SQL Server 2014中的一个实现。这个例子有点进化,使用from和to来动态过滤结果

    -- Dynamic MS SQL Example using recursive CTE
    DECLARE @FromDate Date = '2016-01-01'
    DECLARE @ToDate Date = '2016-12-31'
    ;
    WITH Months(Year, Month, Date) AS
    (
        SELECT Year(@FromDate), Month(@FromDate), @FromDate
        UNION ALL
        SELECT Year(NextMonth.Date), Month(NextMonth.Date), NextMonth.Date
        FROM Months
        CROSS APPLY (SELECT DateAdd(m, 1, Date) Date) NextMonth
        WHERE NextMonth.Date < @ToDate
    )
    SELECT Months.Year, Months.Month, COUNT(*) as 'NumberOfAppointments'
    FROM Months
    LEFT OUTER JOIN appointment ON Year(dateofappointment) = Months.Year AND Month(dateofappointment) = Months.Month
    GROUP BY Months.Year, Months.Month