一道mysql查詢面試題的思考解決過程

囉嗦(可跳過)

前幾天面試,筆試時遇到這道題,讀了幾遍題目都是懵懵懂懂,「一個段時間內至少N天,這N天中天天的分數總和要大於M」,好繞,最後沒有寫對。mysql

今天想起來這道題,寫出了答案並進行了sql語句的驗證。面試

問題

某遊戲使用mysql數據庫,數據表 scores 記錄用戶得分歷史,uid 表明用戶ID, score 表示分數, date 表示日期,每一個用戶天天都會產生多條記錄。sql

數據結構以及數據行以下:數據庫

uid int(11) score int(11) date date
1 2 2017-02-28
1 3 2017-03-02
3 2 2017-03-17
3 1 2017-03-17
3 2 2017-03-17
4 3 2017-03-25
3 5 2017-03-27
... ... ...

如今須要一份用戶列表,這些用戶在2017年3月份的31天中,至少要有16天,天天得分總和大於40分。使用一條sql語句表示。數據結構

思路

從新梳理需求,畫出重點。函數

如今須要一份用戶列表,這些用戶在2017年3月份的31天中至少要有16天天天得分總和大於40分。使用一條sql語句表示。ui

用戶列表
表明一個不重複的 uid 列表,可以使用 DISTINCT uidGROUP BY uid 來實現。調試

在2017年3月份的31天中
使用 where 語句限定時間範圍。code

至少要有16天
須要對天 date 進行聚合,使用聚合函數 COUNT(*) > 15來進行判斷。 遊戲

(每人)天天得分總和大於40
須要對天天分數 score 分數進行聚合,使用聚合函數對 SUM(score) > 40來進行判斷。

此處有2處聚合函數,可是是針對不一樣維度的(天和天天裏的分數),因此須要使用子查詢,將2處聚合分別放置在內外層的sql語句上。

由「從內到外」的原則,咱們先對天天的得分進行聚合,那就是對天進行聚合。

-- 在2017年3月份的31天中
select * from scores where `date` >= '2017-03-01' and `date` <= '2017-03-31';


-- (每人)天天得分總和大於40
-- 使用 group by uid,date 實現對分數進行聚合,使用 having  sum() 過濾結果
select uid,date from scores where `date` >= '2017-03-01' and `date` <= '2017-03-31' group by uid, `date` having sum(score) > 40;

-- 至少要有16天
-- 以上條結果爲基礎,在對 group by uid 實現對天進行聚合,使用 having  count() 過濾結果
select uid from (
    select uid,date from scores where `date` >= '2017-03-01' and `date` <= '2017-03-31' group by uid, `date` having sum(score) > 40
) group by uid having count(*) > 15;

答案

SELECT uid FROM (
    SELECT uid,date FROM WHERE `date` >= '2017-03-01' AND `date` <= '2017-03-31' GROUP BY uid,`date` HAVING SUM(score) > 40
) WHERE GROUP BY uid HAVING count(*) > 15;

驗證

-- 結構
CREATE TABLE `scores` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` int(11) DEFAULT NULL,
  `score` int(11) DEFAULT NULL,
  `date` date DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 數據
INSERT INTO `scores` VALUES ('1', '1', '1', '2018-04-03');
INSERT INTO `scores` VALUES ('2', '1', '2', '2018-04-03');
INSERT INTO `scores` VALUES ('3', '1', '1', '2018-04-04');
INSERT INTO `scores` VALUES ('11', '1', '4', '2018-04-04');
INSERT INTO `scores` VALUES ('12', '1', '3', '2018-04-06');
INSERT INTO `scores` VALUES ('4', '1', '3', '2018-04-07');
INSERT INTO `scores` VALUES ('5', '2', '2', '2018-04-04');
INSERT INTO `scores` VALUES ('6', '2', '4', '2018-04-04');
INSERT INTO `scores` VALUES ('7', '2', '1', '2018-04-03');
INSERT INTO `scores` VALUES ('8', '3', '3', '2018-04-06');
INSERT INTO `scores` VALUES ('9', '3', '1', '2018-04-05');
INSERT INTO `scores` VALUES ('10', '3', '2', '2018-04-04');

-- 由於數據錄入量有限,咱們將結果改成修改改成:
-- 獲取一個用戶列表,時間範圍是4號到6號,至少要有2天,天天分數總和大於2。

-- 查詢
-- 非最精簡語句,包含調試語句,可分段運行查看各個語句部分的效果。
SELECT
    uid
FROM
    (
        SELECT
            uid,
            `date`,
            sum(score) AS total_score
        FROM
            scores
        WHERE
            `date` > '2018-04-03'
        AND `date` < '2018-04-07'
        GROUP BY
            uid,
            `date`
        HAVING
            total_score > 2
        ORDER BY
            uid,
            date
    ) AS a
GROUP BY
    uid
HAVING
    count(*) > 1;

-- 答案是:
uid : 1
相關文章
相關標籤/搜索