數據庫EER圖
數據庫表、字段、約束解釋
users 用戶:
id 標識符,email 郵箱,password 密碼,name 姓名,sex 性別,enabled 啟用 ,role 角色
id primary key
advisors 指導:
id 標識符,uid_user 被指導者,uid_admin 指導者
id primary key
uid_user -> user(id),uid_admin(id)
categories 類別:
id 標識符,name 名稱,description 描述
id primary key
questions 問題:
id 標識符,qid 題號,description 描述,cid 類別標識符,name 名稱
id primary key
cid -> categories(id)
test_data 測試數據:
id 標識符,qid 題號,input 一組輸入,output 一組輸出
id primary key
qid -> questions(id)
tests 測試信息:
id 標識符,uid 用戶標識符,qid 題號,submit_time 提交時間,code 代碼,correct_rate 正確率
id primary key
uid -> users(id),qid -> questions(id)
scores 成績:
id 標識符,uid 用戶標識符,cid 類別標識符,usual_score 平時成績
復雜SQL語句
selectSumScoreAndRank:
功能:根據用戶 ID,查詢用戶 ID、用戶所有題目總成績、總成績的排名。
實現:主要是利用了聚集函數,MySQL 自帶 @rowNum 屬性。
代碼:
SELECT * FROM (SELECT uid, sum_correct_rate, (@rowNum := @rowNum + 1) AS rank FROM (SELECT uid, sum(max_corrcet_rate) AS sum_correct_rate FROM (SELECT uid, qid, max(correct_rate) AS max_corrcet_rate FROM tests GROUP BY uid, qid) AS max_tests GROUP BY uid ORDER BY sum_correct_rate DESC) AS rank_tests, (SELECT (@rowNum := 0)) AS rank) AS all_tests WHERE uid = #{uid}
selectPracticeAndUsualScoreFromAdmin:
功能:根據管理員 ID 和類別 ID,查詢特定管理員的指導關系下用戶的實踐成績和平時成績。
實現:首先根據指導關系下的用戶 ID 和特定題庫 ID 選出特定題目,再根據特定題目選出特定測試,最后利用聚集函數進行成績整合(例如,聚集函數 sum 計算特定用戶特定題庫下的總分)。
代碼:
SELECT scores.id, scores.uid, users.email, users.name AS userName, sum_tests.avg_correct_rate * 100 AS practice_score, scores.usual_score FROM (SELECT uid, avg(max_corrcet_rate) AS avg_correct_rate FROM (SELECT uid, qid, max(correct_rate) AS max_corrcet_rate FROM (SELECT uid, qid, correct_rate FROM tests WHERE uid IN (SELECT uid_user FROM advisors WHERE uid_admin = #{uidAdmin}) AND qid IN (SELECT questions.id FROM categories LEFT JOIN questions ON categories.id = questions.cid WHERE cid = #{cid}) ) AS filter_tests GROUP BY uid, qid) AS max_tests GROUP BY uid) AS sum_tests LEFT JOIN scores ON sum_tests.uid = scores.uid AND scores.cid = #{cid} LEFT JOIN users ON sum_tests.uid = users.id
設計思想
1、為什么要每張表都有 ID,並且把 ID 作為主鍵?
表的主鍵不應該可以變動的,而現實中的需求會變動。起初,表 questions 是沒有列 cid 的,后來為了模擬現實中題目(questions)的類別(categories), 增加了 cid 列。
假設有一種情況:
類別名(categories name)為 Java,題號(qid)為 1,2,3;類別為 C#,題號為1,2,3。
如果表 questions 以 qid 作為主鍵,上述的情況是無法實現的,因為primary key 違反了唯一性約束,需要重新設計架構;如果表 questions 以無意義的 id 作為主鍵,上述情況實現很簡單,不需要變動架構。
所以,表的主鍵最好是無意義的id。
2、表 questions 和表 test_data 的設計
表 questions 起初和 test_data 是放在一起的,即 input 和 output 起初是在表 questions 中的,並且每條記錄表示的多組輸入和多組輸出。后來我剝離了,並且將每條記錄由多組輸入和多組輸出變為一組輸入和一組輸出,原因如下:
① 多組的輸入或者多組的輸出不方便保存。如果合並為一組保存,必須以一個符號作為分隔符,然而在 OJ 系統,任何符號的輸入都是有可能的,分隔符無法選擇
② 如果采用多組保存,冗余性較高,qid、name等多保存了很多次。
所以,我采取弱關聯(將多值屬性剝離,新建一個表存入,新表高度依賴於原來的表)來保存。