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

Mysql分組查詢每組最新的一條數(shù)據(jù)的五種實現(xiàn)方法

 更新時間:2024年08月04日 09:32:10   作者:kerwin_code  
在寫報表功能時遇到一個需要根據(jù)用戶id分組查詢最新一條錢包明細數(shù)據(jù)的需求,本文主要介紹了Mysql分組查詢每組最新的一條數(shù)據(jù)的五種實現(xiàn)方法,感興趣的可以了解一下

前言

在寫報表功能時遇到一個需要根據(jù)用戶id分組查詢最新一條錢包明細數(shù)據(jù)的需求,在寫sql測試時遇到一個有趣的問題,開始使用子查詢根據(jù)時間倒序+group by customer_id發(fā)現(xiàn)查詢出來的數(shù)據(jù)一直都是最舊的一條,而不是我需要的最新一條數(shù)據(jù)我明明已經(jīng)倒序排了,后來總結出了五種解決方案如下。

注意事項

數(shù)據(jù)庫版本 Mysql5.7+

執(zhí)行 GROUP BY 語句的時候出現(xiàn) sql_mode=only_full_group_by 解決方法(這里是Mysql8的解決方案,Mysql5.7也差不多,具體實現(xiàn)可以查看 解決MySQL-this is incompatible with sql_mode=only_full_group_by 問題

1、執(zhí)行 select @@sql_mode; 查看sql模式

select @@sql_mode;

在這里插入圖片描述

2、將sql_mode中的only_full_group_by模式剔除 重新設置sql_mode值,如果是使用JDBC連接需要重啟項目才能生效。

set global sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
set session sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';

準備SQL

這里模擬一個sql

DROP TABLE IF EXISTS `customer_wallet_detail`;
CREATE TABLE `customer_wallet_detail`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `customer_id` bigint(20) NULL DEFAULT NULL COMMENT '用戶ID',
  `happen_amount` varchar(15)  NULL DEFAULT '0' COMMENT '發(fā)生金額 帶-號的代表扣款',
  `balance_amount` varchar(15) NULL DEFAULT '0' COMMENT '可用余額',
  `create_time` bigint(20) NULL DEFAULT NULL COMMENT '發(fā)生時間',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB COMMENT = '用戶錢包明細';

INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `create_time`) VALUES (1, 1, '100', '100', 1670300656630);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `create_time`) VALUES (2, 1, '-10', '90', 1670300656640);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `create_time`) VALUES (3, 1, '5', '95', 1670300656650);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `create_time`) VALUES (4, 3, '998', '998', 1670300656660);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `create_time`) VALUES (5, 3, '-100', '898', 1670300656670);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `create_time`) VALUES (6, 3, '-98', '800', 1670300656680);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `create_time`) VALUES (7, 2, '666', '666', 1670300656690);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `create_time`) VALUES (8, 2, '-66', '600', 1670300656695);
INSERT INTO `customer_wallet_detail`(`id`, `customer_id`, `happen_amount`, `balance_amount`, `create_time`) VALUES (9, 2, '-600', '0', 1670300656699);

在這里插入圖片描述

錯誤查詢

SELECT
	* 
FROM
	( SELECT * FROM customer_wallet_detail ORDER BY create_time DESC ) t1 
GROUP BY
	t1.customer_id;

在這里插入圖片描述

錯誤原因

在mysql5.7以及之后的版本,如果GROUP BY的子查詢中包含ORDER BY,但是 GROUP BY 不與 LIMIT 配合使用,ORDER BY會被忽略掉,所以子查詢在 GROUP BY 時排序不會生效,可能是因為子查詢大多數(shù)是作為一個結果給主查詢使用,所以子查詢不需要排序。

方法一

鑒于以上的原因我們可以添加上 LIMIT 條件來實現(xiàn)功能。
PS:這個LIMIT的數(shù)量可以先自行 COUNT 出你要遍歷的數(shù)據(jù)條數(shù)(這個數(shù)據(jù)條數(shù)是所有滿足查詢條件的數(shù)據(jù)合,我這里共9條數(shù)據(jù))

SELECT
	* 
FROM
	( SELECT * FROM customer_wallet_detail ORDER BY create_time DESC LIMIT 9 ) t1 
GROUP BY
	t1.customer_id;

在這里插入圖片描述

方法二(適用于自增ID和創(chuàng)建時間排序一致)

方法一需要先 COUNT 查詢然后將查詢結果設置到 LIMIT 條件中比較麻煩,這里還可以使用 MAX() 函數(shù)來實現(xiàn)該功能。
PS:因為我這里的業(yè)務數(shù)據(jù)是有序插入的,使用主鍵自增id和create_time結果是一樣的而且使用id查詢效率更高,如果沒有唯一且有序的id可以替代create_time那么就用方案一,不能直接使用 SELECT id,MAX(create_time) 這種操作來獲取最新一條數(shù)據(jù)id,原因在總結中有詳細描述。

SELECT
	*
FROM
	customer_wallet_detail 
WHERE
	id IN ( SELECT MAX( id ) FROM customer_wallet_detail GROUP BY customer_id ) 
ORDER BY
	customer_id;

在這里插入圖片描述

方法三(適用于自增ID和創(chuàng)建時間排序一致,查詢性能最優(yōu))

方法三和方法二實現(xiàn)邏輯基本一致只是將IN查詢替換成了連接查詢,本地20w條數(shù)據(jù)測試 方法三比方法二性能提升50%,有興趣的可以增大數(shù)據(jù)集測試后續(xù)性能變化。

SELECT
	t1.* 
FROM
	customer_wallet_detail t1
	INNER JOIN ( SELECT MAX( id ) AS id FROM customer_wallet_detail GROUP BY customer_id ) t2 ON t1.id = t2.id

在這里插入圖片描述

方法四(通過DISTINCT關鍵字打破MySQL語句優(yōu)化使排序生效)

方法四實現(xiàn)起來比較簡單,數(shù)據(jù)量小的時候查詢性能也挺不錯的,數(shù)據(jù)量大了之后查詢性能也還可以,我本地測試了100w數(shù)據(jù)的查詢,這個方法耗時0.9s左右,減少DISTINCT的字段能降到0.4s左右,不給customer_id字段加索引的情況下通過方法三查詢耗時0.35s,加了索引耗時0.035s,有興趣可以分析一下方法三和方法四的執(zhí)行計劃。

SELECT
	* 
FROM
	( SELECT DISTINCT * FROM `customer_wallet_detail` ORDER BY id DESC ) AS t1 
GROUP BY
	t1.customer_id;

在這里插入圖片描述

方法五(以創(chuàng)建時間為基準獲取每個用戶最新的一條數(shù)據(jù),必須要添加對應字段的索引 最好是覆蓋索引)

有朋友在評論區(qū)提供了第四種方法,這種方法在表數(shù)據(jù)量少的時候是可行的,我的測試表還是20w數(shù)據(jù),并且customer_id字段加了索引,全部查詢出來耗時在180s左右,我本地MySQL性能會差一點,這種查詢方式是將 b1 中的每一條數(shù)據(jù) 都和 b2 中的每一條數(shù)據(jù)進行比對取出滿足條件的數(shù)據(jù),b1 有20w條數(shù)據(jù) b2 也有20w條數(shù)據(jù),如果沒有索引不計算io開銷,只算cpu開銷,這條sql需要進行 20w * 20w = 400億次數(shù)據(jù)比對,在有索引的情況下數(shù)據(jù)比對次數(shù)會少一些但是也千萬級的,如果考慮其它開銷并且沒索引的情況下那查詢耗時可想而知。

使用限制

  • 1、這種方式其實除了性能問題以外還有一個更加嚴重的問題,在一些業(yè)務里給用戶余額明細添加數(shù)據(jù)時可能同一時間戳添加多條,這樣count結果就大于1了,這個用戶數(shù)據(jù)就查不出來了,還有一種情況如果開發(fā)人員事務沒有控制好,我們在入庫時一般會提前將create_time填充,但是我們用的是自增ID,入庫時create_time 小的,數(shù)據(jù)ID可能還會大一些,選擇那種方法還是需要看業(yè)務上怎么設計的
SELECT
	b1.* 
FROM
	customer_wallet_detail t1 
WHERE
	( SELECT COUNT( 1 ) FROM customer_wallet_detail t1 WHERE t2.customer_id = t1.customer_id AND t1.create_time <= t2.create_time ) <= 1;

在這里插入圖片描述

PS:優(yōu)化方案

  • 1、針對這條語句的查詢特性,我們減少數(shù)據(jù)的查詢條數(shù),比如給 t1和t2 添加上篩選時間區(qū)間,減少遍歷數(shù)組總數(shù)。
  • 2、使用覆蓋索引,我自己在測試的時候發(fā)現(xiàn)如果使用組合索引包含兩個字段 (customer_id,create_time) 性能會提升很多,20w數(shù)據(jù)查詢出結果只用了40s,如果只使用customer_id字段索引會進行回表,使用覆蓋索引沒有額外的回表操作所以會快很多。

總結

結合我的業(yè)務經(jīng)過測試,目前看來方案三是最合適的,sql簡單性能適中,方案一比方案二性能更差而且實現(xiàn)麻煩,最終選擇那個方案主要看業(yè)務而定。

MAX()函數(shù)和MIN()這一類函數(shù)和GROUP BY配合使用存在問題

MAX()函數(shù)和MIN()這一類函數(shù)和GROUP BY配合使用,GROUP BY拿到的數(shù)據(jù)永遠都是這個分組排序最上面的一條,而MAX()函數(shù)和MIN()這一類函數(shù)會將這個分組中最大或最小的值取出來,這樣會導致查詢出來的數(shù)據(jù)對應不上。

正確查詢:

在這里插入圖片描述

錯誤查詢:這里的確拿到每個分組最新創(chuàng)建時間了但是拿的數(shù)據(jù)id還是排序的第一條

在這里插入圖片描述

在這里插入圖片描述

到此這篇關于Mysql分組查詢每組最新的一條數(shù)據(jù)的五種實現(xiàn)方法的文章就介紹到這了,更多相關Mysql分組查詢內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家! 

相關文章

  • Mysql?刪除重復數(shù)據(jù)保留一條有效數(shù)據(jù)(最新推薦)

    Mysql?刪除重復數(shù)據(jù)保留一條有效數(shù)據(jù)(最新推薦)

    這篇文章主要介紹了Mysql?刪除重復數(shù)據(jù)保留一條有效數(shù)據(jù),實現(xiàn)原理也很簡單,mysql刪除重復數(shù)據(jù),多個字段分組操作,結合實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2023-02-02
  • SpringBoot項目中MySQL索引失效的常見場景與解決方案

    SpringBoot項目中MySQL索引失效的常見場景與解決方案

    在SpringBoot項目開發(fā)中,我們通常使用JPA、MyBatis等ORM框架與MySQL數(shù)據(jù)庫交互,雖然這些框架極大地提高了開發(fā)效率,但容易寫出索引失效的查詢語句,導致系統(tǒng)性能急劇下降,所以本文將詳細介紹SpringBoot項目中常見的MySQL索引失效場景,并提供解決方案和最佳實踐
    2025-09-09
  • mysql 8.0.18各版本安裝及安裝中出現(xiàn)的問題(精華總結)

    mysql 8.0.18各版本安裝及安裝中出現(xiàn)的問題(精華總結)

    這篇文章主要介紹了mysql 8.0.18各版本安裝及安裝中出現(xiàn)的問題,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-12-12
  • MySQL出現(xiàn)SQL Error (2013)連接錯誤的解決方法

    MySQL出現(xiàn)SQL Error (2013)連接錯誤的解決方法

    這篇文章主要介紹了MySQL出現(xiàn)SQL Error (2013)連接錯誤的解決方法,2013錯誤主要還是在于用戶的授權問題,需要的朋友可以參考下
    2016-06-06
  • MySQL設置表自增步長的方法

    MySQL設置表自增步長的方法

    自增字段是一種常見且重要的功能,通常用于生成唯一的標識符,本文主要介紹了MySQL設置表自增步長的方法,具有一定的參考價值,感興趣的可以了解一下
    2024-08-08
  • mysql字符串函數(shù)詳細匯總

    mysql字符串函數(shù)詳細匯總

    這篇文章主要介紹了mysql字符串函數(shù)詳細匯總,字符串函數(shù)主要用來處理數(shù)據(jù)庫中的字符串數(shù)據(jù),更多相關內容需要的朋友可以參考一下
    2022-07-07
  • 故障的機器修好后重啟,狂拉主庫binlog,導致網(wǎng)絡問題的解決方法

    故障的機器修好后重啟,狂拉主庫binlog,導致網(wǎng)絡問題的解決方法

    本文主要記錄一次簡單的、典型的故障,發(fā)生問題的原因很簡單,這個問題發(fā)生也很簡單,各位同學一定要注意,一不留神就會對主庫造成影響
    2016-04-04
  • mysql日期和時間的間隔計算實例分析

    mysql日期和時間的間隔計算實例分析

    這篇文章主要介紹了mysql日期和時間的間隔計算,結合實例形式分析了mysql日期和時間間隔計算的相關操作技巧與注意事項,需要的朋友可以參考下
    2019-12-12
  • MySQL常見數(shù)值函數(shù)整理

    MySQL常見數(shù)值函數(shù)整理

    MySQL中另外一類很重要的函數(shù)就是數(shù)值函數(shù),這些函數(shù)能處理很多數(shù)值方面的運算,下面這篇文章主要給大家介紹了關于MySQL常見數(shù)值函數(shù)整理的相關資料,需要的朋友可以參考下
    2023-02-02
  • MySQL分區(qū)之KEY分區(qū)詳解

    MySQL分區(qū)之KEY分區(qū)詳解

    按照key進行分區(qū)非常類似于按照hash進行分區(qū),只不過hash分區(qū)允許使用用戶自定義的表達式,下面這篇文章主要給大家介紹了關于MySQL分區(qū)之KEY分區(qū)的相關資料,需要的朋友可以參考下
    2022-04-04

最新評論

武鸣县| 富蕴县| 班戈县| 鄢陵县| 平利县| 新乡市| 新河县| 清流县| 同江市| 宜城市| 镇雄县| 双辽市| 津南区| 万荣县| 安庆市| 综艺| 新疆| 宝兴县| 西城区| 大方县| 老河口市| 海城市| 永清县| 通城县| 阿坝县| 葫芦岛市| 多伦县| 广元市| 泸溪县| 赣州市| 南木林县| 明溪县| 邯郸县| 夏邑县| 宁阳县| 赤城县| 宁波市| 依兰县| 含山县| 福鼎市| 长宁县|