最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

從入門到高階實(shí)戰(zhàn)詳解如何使用MySQL做數(shù)據(jù)統(tǒng)計(jì)

 更新時(shí)間:2026年03月06日 08:17:19   作者:detayun  
這篇文章主要為大家詳細(xì)介紹了MySQL中統(tǒng)計(jì)分析的核心功能,包括基礎(chǔ)聚合函數(shù),分組統(tǒng)計(jì),窗口函數(shù)(8.0+以及性能優(yōu)化策略,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下

摘要:數(shù)據(jù)統(tǒng)計(jì)是后端開發(fā)和數(shù)據(jù)分析的高頻需求。是用代碼在內(nèi)存里算,還是讓數(shù)據(jù)庫算?本文深入講解 MySQL 統(tǒng)計(jì)分析的核心能力,涵蓋聚合函數(shù)、分組匯總、去重計(jì)數(shù)、窗口函數(shù)以及性能優(yōu)化策略,幫你搞定 90% 的數(shù)據(jù)統(tǒng)計(jì)場景。

一、 前言:別把計(jì)算壓力扔給應(yīng)用層

很多初級開發(fā)在做統(tǒng)計(jì)時(shí),習(xí)慣把數(shù)據(jù)全查出來,在 Java/Python/Go 代碼里循環(huán)計(jì)算:

# ? 錯(cuò)誤示范:把 100 萬行數(shù)據(jù)拉到內(nèi)存里算
rows = db.query("SELECT * FROM orders WHERE status='paid'")
total = 0
for row in rows:
    total += row.amount

這種做法不僅慢,還會把應(yīng)用服務(wù)器的內(nèi)存打爆。MySQL 內(nèi)置了強(qiáng)大的統(tǒng)計(jì)函數(shù),讓數(shù)據(jù)在磁盤端就完成計(jì)算,只返回結(jié)果集,這才是正確姿勢。

二、 基礎(chǔ)篇:核心聚合函數(shù)

這是統(tǒng)計(jì)的基石,必須熟練掌握。

1. COUNT 系列:數(shù)數(shù)的藝術(shù)

  • COUNT(*):統(tǒng)計(jì)行數(shù)(不關(guān)心 NULL,性能最優(yōu))。
  • COUNT(field):統(tǒng)計(jì)指定字段非 NULL 的行數(shù)。
  • COUNT(DISTINCT field):統(tǒng)計(jì)去重后的數(shù)量。

坑點(diǎn)SELECT COUNT(*) FROM table 在 InnoDB 中其實(shí)很快(不像 MyISAM 那樣存了元數(shù)據(jù),但 InnoDB 會走最小的二級索引),千萬別為了加速而用 COUNT(1),優(yōu)化器會自動把它們變成一樣的執(zhí)行計(jì)劃。

2. SUM / AVG / MAX / MIN

最基礎(chǔ)的求和與極值。

-- 統(tǒng)計(jì)近 7 天的總銷售額和平均客單價(jià)
SELECT 
    SUM(amount) as total_sales,
    AVG(amount) as avg_price,
    MAX(create_time) as last_order_time
FROM orders 
WHERE create_time > NOW() - INTERVAL 7 DAY;

三、 進(jìn)階篇:分組與多維分析

單看總數(shù)沒意義,我們需要按維度拆解。

1. GROUP BY:按維度切分

-- 按城市統(tǒng)計(jì)用戶數(shù)
SELECT city, COUNT(*) 
FROM users 
GROUP BY city;

優(yōu)化技巧GROUP BY 的字段必須建立索引,否則會產(chǎn)生臨時(shí)表(Using temporary)和文件排序(Using filesort),性能極差。

2. WITH ROLLUP:小計(jì)與總計(jì)

如果你需要“各城市小計(jì) + 全國總計(jì)”,不需要寫兩條 SQL,用 WITH ROLLUP 一把梭:

SELECT city, COUNT(*) 
FROM users 
GROUP BY city WITH ROLLUP;

結(jié)果會多一行 NULL,這就是總計(jì)。

3. GROUPING SETS:多維度交叉

假設(shè)你要同時(shí)看“按性別統(tǒng)計(jì)”和“按年齡統(tǒng)計(jì)”,傳統(tǒng)寫法要 UNION ALL,現(xiàn)在可以用:

SELECT gender, age_group, COUNT(*)
FROM users
GROUP BY GROUPING SETS ( (gender), (age_group) );

四、 高階篇:窗口函數(shù)(MySQL 8.0+)

這是現(xiàn)代 SQL 的大殺器,能解決“排名”、“累計(jì)”、“移動平均”等難題,無需自連接。

1. 排名問題:誰是 Top N?

-- 查詢每個(gè)部門工資前 3 名的員工(允許并列)
SELECT name, department, salary,
       DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) as ranking
FROM employees;
  • ROW_NUMBER():不重復(fù)排名 (1, 2, 3)
  • RANK():跳躍排名 (1, 1, 3)
  • DENSE_RANK():連續(xù)排名 (1, 1, 2)

2. 累計(jì)與移動平均

-- 計(jì)算每日銷售額及近 3 天移動平均(MA3)
SELECT date, sales,
       AVG(sales) OVER (
           ORDER BY date 
           ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
       ) as moving_avg_3d
FROM daily_report;

3. 同比/環(huán)比

利用 LAG() 函數(shù)取上一行的值:

SELECT date, sales,
       LAG(sales, 1) OVER (ORDER BY date) as prev_day_sales,
       (sales - LAG(sales, 1) OVER (ORDER BY date)) / LAG(sales, 1) OVER (ORDER BY date) * 100 as growth_rate
FROM daily_report;

五、 實(shí)戰(zhàn)篇:時(shí)間序列統(tǒng)計(jì)

做報(bào)表時(shí),最怕數(shù)據(jù)“斷檔”。比如統(tǒng)計(jì)最近 30 天的日活,如果某天沒數(shù)據(jù),SQL 不會返回 0,而是直接跳過這一天。

解決方案:補(bǔ)全時(shí)間軸

MySQL 本身沒有生成序列的函數(shù)(不像 PostgreSQL 的 generate_series),我們需要用“數(shù)字表”或者遞歸 CTE 來制造一個(gè)連續(xù)的時(shí)間序列,然后左連接業(yè)務(wù)表。

-- 1. 生成連續(xù)日期(遞歸 CTE,MySQL 8.0+)
WITH RECURSIVE date_series AS (
    SELECT CURDATE() - INTERVAL 29 DAY as dt
    UNION ALL
    SELECT dt + INTERVAL 1 DAY FROM date_series WHERE dt < CURDATE()
)
-- 2. 左連接業(yè)務(wù)表,用 IFNULL 補(bǔ) 0
SELECT 
    d.dt,
    COUNT(o.id) as order_count, -- 這里統(tǒng)計(jì)的是左連接后的非空行
    IFNULL(COUNT(o.id), 0) as real_count -- 更嚴(yán)謹(jǐn)?shù)膶懛ㄆ鋵?shí)是 SUM(CASE WHEN o.id IS NOT NULL THEN 1 ELSE 0 END)
FROM date_series d
LEFT JOIN orders o ON DATE(o.create_time) = d.dt
GROUP BY d.dt
ORDER BY d.dt;

注:如果是低版本 MySQL,需要建一張輔助的數(shù)字表(Numbers Table)。

六、 性能優(yōu)化:讓統(tǒng)計(jì)飛起來

統(tǒng)計(jì)查詢通常涉及全表掃描或大范圍掃描,如何優(yōu)化?

1.覆蓋索引(Covering Index)

如果統(tǒng)計(jì)只涉及索引列,MySQL 就不需要回表查數(shù)據(jù)行。

-- 假設(shè)有索引 idx_city (city)
SELECT city, COUNT(*) FROM users GROUP BY city; -- 極快,只掃索引

2.避免 SELECT

統(tǒng)計(jì)時(shí)只查需要的字段,減少網(wǎng)絡(luò)傳輸和內(nèi)存開銷。

3.近似計(jì)算

如果不需要 100% 精確(如 UV 統(tǒng)計(jì)),用 APPROX_DISTINCT(基于 HyperLogLog 算法)比 COUNT(DISTINCT) 快 10 倍以上,且內(nèi)存占用極小。

SELECT APPROX_DISTINCT(user_id) FROM logs;

4.分表/分庫/OLAP

如果單表數(shù)據(jù)量過億,統(tǒng)計(jì)壓力大,考慮分庫分表。

如果統(tǒng)計(jì)邏輯極其復(fù)雜(多表關(guān)聯(lián)、Ad-hoc 查詢),不要死磕 MySQL。將數(shù)據(jù)同步到 ClickHouse、Doris 或 TiDB 這類 OLAP 數(shù)據(jù)庫,查詢速度能提升 10-100 倍。

七、 總結(jié):統(tǒng)計(jì)函數(shù)速查表

需求場景推薦函數(shù)/語法備注
簡單計(jì)數(shù)/求和COUNT, SUM, AVG基礎(chǔ)中的基礎(chǔ)
分組統(tǒng)計(jì)GROUP BY必須帶索引
多級匯總WITH ROLLUP替代多次查詢
排名/TopNRANK(), DENSE_RANK()窗口函數(shù)
累計(jì)/移動平均SUM() OVER (... ROWS BETWEEN)窗口函數(shù)
同比/環(huán)比LAG(), LEAD()窗口函數(shù)
近似去重APPROX_DISTINCT大數(shù)據(jù)量首選
時(shí)間軸補(bǔ)全遞歸 CTE + Left Join解決數(shù)據(jù)斷層

最后的一句話:MySQL 不僅是一個(gè) OLTP(事務(wù)處理)數(shù)據(jù)庫,掌握好上述統(tǒng)計(jì)技巧,它也能勝任輕量級的 OLAP(分析)工作。但如果你的業(yè)務(wù)是“雙十一大屏實(shí)時(shí)監(jiān)控”或者“每天跑幾百個(gè)復(fù)雜報(bào)表”,請果斷上 ClickHouse。

到此這篇關(guān)于從入門到高階實(shí)戰(zhàn)詳解如何使用MySQL做數(shù)據(jù)統(tǒng)計(jì)的文章就介紹到這了,更多相關(guān)MySQL數(shù)據(jù)統(tǒng)計(jì)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

伽师县| 黄骅市| 宁蒗| 阳信县| 鹤壁市| 盐池县| 乃东县| 怀安县| 治县。| 阜阳市| 大城县| 东光县| 闽清县| 潜江市| 礼泉县| 锦屏县| 且末县| 精河县| 合阳县| 四川省| 广德县| 集贤县| 冕宁县| 池州市| 攀枝花市| 吉隆县| 嘉祥县| 肇州县| 晋中市| 浠水县| 睢宁县| 交城县| 莎车县| 信阳市| 河曲县| 普定县| 台北县| 军事| 简阳市| 平湖市| 崇左市|