1. 程式人生 > 其它 >【實戰分享】相同組獲取第一條資料的幾種SQL實現

【實戰分享】相同組獲取第一條資料的幾種SQL實現

技術標籤:sqldbjavasqldb資料庫java相同組獲取第一條資料

【實戰分享】相同組獲取第一條資料的幾種SQL實現

  • 分組排序後獲取第一條資料
  • 分組加行號後獲取第一條資料(當然獲取前幾條資料也是同理)
  1. 利用GROUP BY後聚合函式,比如:MAX, MIN等

此種方式簡單粗暴,存在一個隱藏問題,就是非分組列獲取值不一定是同一行的資料,比如:這裡的workout_time
此種方式適用於MariaDB,如果想適用於MySQL需要修改ANY_VALUE(wr.workout_date),存在非分組列相同問題

SELECT       
  h.id AS 'hospital_id'
, h.name AS 'hospital_name', p.id AS 'patient_id', p.name AS 'patient_name', ed.game_id, g.name AS 'game_name', MAX(ed.score) AS 'game_score', wr.workout_date AS 'workout_time' FROM t_p p, t_w_r wr, t_e_d ed, t_h h, t_g g WHERE p.name =
'kenny' AND p.id = wr.patient_id AND wr.id = ed.workout_id AND p.hospital_id = h.id AND ed.game_id = g.id GROUP BY p.id, p.name, ed.game_id, workout_time ORDER BY game_id, game_score DESC;
  1. ROW_NUMBER() OVER(PARTITION BY [fields] ORDER BY [fields]) AS ‘row_num’

此種方式適用於MariaDB,不適用在低版本MySql,聽說8.0以上支援

SELECT * from (
SELECT ROW_NUMBER() OVER(PARTITION BY p.id, p.name, ed.game_id ORDER BY game_id, game_score DESC) AS 'row_num',
  h.id AS 'hospital_id', h.name AS 'hospital_name', p.id AS 'patient_id', p.name AS 'patient_name', p.gender,          
  ed.game_id, g.name AS 'game_name', g.limb, ed.score AS 'game_score', wr.workout_date AS 'workout_time'     
FROM       
  t_p p,       
  t_w_r wr,      
  t_e_d ed,       
  t_h h,      
  t_g g      
WHERE  
  -- p.name = 'kenny' and
  ed.game_id = 28 and
  workout_date >= '2019-09-11' and
  p.id = wr.patient_id   
  AND wr.id = ed.workout_id       
  AND p.hospital_id = h.id       
  AND ed.game_id = g.id            
  ORDER BY game_id, game_score DESC
) t
WHERE row_num = 1
  1. SET @row_number = 0; SELECT @row_number:=IF(@pre_game_id = game_id, @row_number + 1, 1) AS 'row_num', @pre_game_id:=game_id AS 'game_id2', t.* FROM WHERE row_num = 1

此種方式適用於MySQL,但是,在MariaDB中使用需要解決一個問題,明明子查詢已經排序再設定行號,但是就是設定行號的時候不是按照排序後的,可以說行號設定在排序前進行了,因此出來的結果同一個分組同時存在幾條row_num = 1的情況
經試驗後,確定MariaDB需要加入GROUP BY方可分組排序後才能正確新增行號
SET @row_number = 0; 另外,這句不加,如果在Client第一次執行會出現行號都是1的情況
經試驗後,是因為@pre_game_id沒有初始值,而@row_number可以沒有初始值

SET @row_number = 0;

SELECT * FROM (
SELECT @row_number:=IF(@pre_game_id = game_id, @row_number + 1, 1) AS 'row_num', t.*, @pre_game_id:=game_id AS 'game_id2' FROM (
SELECT 
  h.id AS 'hospital_id', h.name AS 'hospital_name', p.id AS 'patient_id', p.name AS 'patient_name', p.gender,          
  ed.game_id, g.name AS 'game_name', g.limb, ed.score AS 'game_score', wr.workout_date AS 'workout_time'     
FROM       
  t_p p,       
  t_w_r wr,      
  t_e_d ed,       
  t_h h,      
  t_g g      
WHERE  
  p.name = 'kenny' AND
  workout_date >= '2010-09-11' AND
  p.id = wr.patient_id   
  AND wr.id = ed.workout_id       
  AND p.hospital_id = h.id       
  AND ed.game_id = g.id 
  -- MySQL 不需要此 GROUP BY,加了會報錯 this is incompatible with sql_mode=only_full_group_by
  GROUP BY game_id, game_score, workout_time       
  ORDER BY game_id, game_score DESC, workout_time DESC
) t
) m
WHERE 
  row_num = 1
  -- MariaDB 不需要此 ORDER BY,加了也沒關係
  ORDER BY game_id

注意:

  1. 如果以上試驗有錯誤或跟實際情況有出入,請試驗正確後採用
  2. 如果考慮資料庫效能,也可以根據業務需要拆分到程式中實現
  3. 不建議一開始就在程式中寫N多步驟的程式碼才東拼西湊,還不如簡單粗暴一個SQL實現效能棒

©Kenny Fang