MySQL group by date,如果不存在则强制返回null值

时间:2015-10-22 13:26:32

标签: mysql

我构建了一个查询,让我返回某些应用的平均排名。

但是对于他们中的一些人来说,有一个月,我们没有数据,因为该应用程序是一个新的(比方说,该应用程序自本月以来已经出局,所以我们自本月以来收集数据)< / p>

SELECT 
    DATE_FORMAT(date, '%Y-%m'),
    app_id,
    AVG(rank)
FROM wadstats.applestore_ranking
where app_id IN (100,
        2,
        3,
        4,
        5,
        6)
GROUP BY MONTH(date), app_id
ORDER BY CASE WHEN app_id = 100 THEN 1 ELSE 2 END, date ASC

我需要首先显示app_id = 100

但是对于app_id = 8,我没有例证8月的数据。

然后结果看起来像

'2015-07', '100', '3.9355'
'2015-04', '100', '49.5000'
'2015-08', '100', '5.2258'
'2015-05', '100', '16.3333'
'2015-09', '100', '6.1333'
'2015-06', '100', '7.5667'
'2015-10', '100', '5.7727'
'2015-04', '2', '6.0000'
'2015-08', '2', '9.8710'
'2015-05', '2', '6.4667'
'2015-09', '2', '8.9667'
'2015-06', '2', '8.5333'
'2015-10', '2', '9.9545'
'2015-07', '2', '10.5806'
'2015-05', '3', '56.3929'
'2015-09', '3', '55.1667'
'2015-06', '3', '35.2500'
'2015-07', '3', '38.7143'
'2015-04', '3', '38.7500'
'2015-08', '3', '52.5500'
'2015-09', '4', '30.2105'
'2015-06', '4', '27.9231'
'2015-10', '4', '30.0000'
'2015-07', '4', '47.0000'
'2015-08', '4', '32.6818'
'2015-06', '5', '46.8667'
'2015-10', '5', '86.6667'
'2015-07', '5', '63.5185'
'2015-04', '5', '24.2500'
'2015-08', '5', '67.3571'
'2015-10', '6', '30.1818'

我希望每个月都有空,即使这个特定月份没有数据

预期结果

'2015-07', '100', '3.9355'
'2015-04', '100', '49.5000'
'2015-08', '100', '5.2258'
'2015-05', '100', '16.3333'
'2015-09', '100', '6.1333'
'2015-06', '100', '7.5667'
'2015-10', '100', '5.7727'
'2015-04', '2', '6.0000'
'2015-08', '2', '9.8710'
'2015-05', '2', '6.4667'
'2015-09', '2', '8.9667'
'2015-06', '2', '8.5333'
'2015-10', '2', '9.9545'
'2015-07', '2', '10.5806'
'2015-05', '3', '56.3929'
'2015-09', '3', '55.1667'
'2015-06', '3', '35.2500'
'2015-07', '3', '38.7143'
'2015-04', '3', '38.7500'
'2015-08', '3', '52.5500'
'2015-09', '4', '30.2105'
'2015-06', '4', '27.9231'
'2015-05', '4', NULL
'2015-10', '4', '30.0000'
'2015-07', '4', '47.0000'
'2015-08', '4', '32.6818'
'2015-06', '5', '46.8667'
'2015-10', '5', '86.6667'
'2015-07', '5', '63.5185'
'2015-04', '5', '24.2500'
'2015-08', '5', '67.3571'
'2015-04', '6', NULL
'2015-05', '6', NULL
'2015-06', '6', NULL
'2015-07', '6', NULL
'2015-08', '6', NULL
'2015-09', '6', NULL
'2015-10', '6', '30.1818'

如果我需要0而不是NULL,那也没关系,但我需要在数据库中每个月都有,为每个app_id都有价值

非常感谢提前

3 个答案:

答案 0 :(得分:3)

下面的查询使用派生表(别名inr)将YearMonth / app_id组合放在一起。然后,它会在left join中使用它来获取数据,无论是否存在于表applestore_ranking中。

如果要显示零而不是NULL,请使用ifnull()。因此,该部分将成为ifnull(AVG(r.rank),0) as Rank

注意,只需简单地将此阶段放在一起然后突出显示inr选择的代码并查看其简单输出即可。这将使左连接更容易理解。

Helper表的概念一直在sql中使用。有时他们会在飞行中放在一起,然后掉线。其他时候它们是永久性的。

模式

create schema appleSandbox;
use appleSandbox;

-- drop table applestore_ranking;
create table applestore_ranking
(   id int auto_increment primary key,
    app_id int not null,
    date date not null, -- not a great column name
    rank int not null
);
-- truncate table applestore_ranking;
insert applestore_ranking (app_id,date,rank) values
(2,'2015-08-01',1),(2,'2015-09-05',10),(2,'2015-09-12',11),(2,'2015-10-01',14),
(6,'2015-10-01',7),(6,'2015-10-05',6),(6,'2015-10-14',2),
(100,'2015-09-01',16),(100,'2015-10-01',16),(100,'2015-10-05',17),(100,'2015-10-14',18);

create table monthHelper
(   -- load this up with a few years worth
    id int auto_increment primary key,
    theDate date not null, -- slightly better column name   
    wantToSee int not null -- do we want to see it in results or not? 0=no, 1=yes
);

-- note only a few wantToSee have been turned on to 1
insert monthHelper(theDate,wantToSee) values
('2015-05-01',0),('2015-06-01',0),('2015-07-01',0),('2015-08-01',1),('2015-09-01',1),('2015-10-01',1),('2015-11-01',0),('2015-12-01',0),
('2016-01-01',0),('2016-02-01',0),('2016-03-01',0); -- etc

查询

SELECT DATE_FORMAT(inr.theDate, '%Y-%m') as YearMonth, inr.app_id, AVG(r.rank) as Rank
FROM
(   select distinct mh.theDate,r.app_id
    from monthhelper mh
    cross join applestore_ranking r
    where mh.wantToSee=1
    and r.app_id IN (100,2,3,4,5,6)
) inr
left join applestore_ranking r
on r.app_id=inr.app_id and year(inr.theDate)=year(r.date) and month(inr.theDate)=month(r.date)
GROUP BY MONTH(inr.theDate), inr.app_id
ORDER BY CASE WHEN inr.app_id = 100 THEN 1 ELSE 2 END, inr.theDate ASC

结果

+-----------+--------+---------+
| YearMonth | app_id | Rank    |
+-----------+--------+---------+
| 2015-08   |    100 |    NULL |
| 2015-09   |    100 | 16.0000 |
| 2015-10   |    100 | 17.0000 |
| 2015-08   |      2 |  1.0000 |
| 2015-08   |      6 |    NULL |
| 2015-09   |      2 | 10.5000 |
| 2015-09   |      6 |    NULL |
| 2015-10   |      2 | 14.0000 |
| 2015-10   |      6 |  5.0000 |
+-----------+--------+---------+

清理

drop schema AppleSandbox;

答案 1 :(得分:2)

感谢Drew,

这是一种在单个查询中执行的方法,详细解释了我们的详细信息。

非常感谢您使用 CROSS JOIN

的组合提示
   SELECT 
    inr.date AS 'month',
    inr.app_id,
    r.title,
    AVG(r.rank) as 'ranking'    
FROM 
    (
        SELECT
            DISTINCT DATE_FORMAT(mh.date, '%Y-%m') date,
            r.app_id
        FROM 
            applestore_ranking mh
        CROSS JOIN
            applestore_ranking r
        WHERE 
            mh.app_id = 100
        AND
            r.app_id IN (100, 1, 2, 3, 4, 5)
    ) inr
LEFT JOIN
    applestore_ranking r ON r.app_id = inr.app_id
AND inr.date = DATE_FORMAT(r.date, '%Y-%m') 
GROUP BY
    inr.date,
    inr.app_id
ORDER BY
    CASE WHEN inr.app_id = 100 THEN 1 ELSE 2 END

答案 2 :(得分:0)

这取决于你如何实现你的表 - 它是否包含行,即使没有统计数据(因为它尚未下载),或者这些行/日期是否完全省略?对于后者,你可能想要使用“数字技巧”(解释in great detail here on SO),当你的排名为零的行时,你可以简单地坚持LEFT JOIN(见MYSQL Documentation

根据您发布的结果和实际需要的结果进行猜测,您应该选择number trick