事情交代
在日常嘮嗑前先簡(jiǎn)單說一下本文想要記錄的一次 MySQL 技術(shù)回顧:兩張表茵臭,對(duì)其中一張表進(jìn)行分組去重后疫诽,對(duì)結(jié)果集進(jìn)行排序,然后關(guān)聯(lián)另一張表得到最終的結(jié)果集旦委。
當(dāng)時(shí)推翻重來了幾次奇徒,從中也學(xué)到不少 SQL 知識(shí),因此有必要寫成文章系統(tǒng)的給自己一個(gè)交代缨硝。
OK摩钙,接下來先談些感悟。不喜的請(qǐng)略過查辩,謝謝
日常嘮嗑
自從來到這家公司之后胖笛,一直在維護(hù)后臺(tái)系統(tǒng),其中包括一些舊版頁面的整改宜岛、增加新功能等等长踊。熟悉一個(gè)已有的業(yè)務(wù)邏輯較復(fù)雜的系統(tǒng),我個(gè)人是沒有什么經(jīng)驗(yàn)的萍倡。大二進(jìn)入老師的實(shí)驗(yàn)室后一直做的是外包項(xiàng)目身弊,每一個(gè)外包項(xiàng)目都是全新的,都是自己去從頭搭建列敲,所以其中涉及到的代碼我都非常熟悉阱佛。
因此剛來的時(shí)候,拿到這個(gè)項(xiàng)目的源代碼戴而,開始去了解熟悉凑术,發(fā)現(xiàn)其中用到的語言版本、框架版本等都普遍偏低所意,非常不適應(yīng)淮逊,心里也不免有不少抱怨(當(dāng)我看完李笑來先生的《把時(shí)間當(dāng)做朋友》后催首,深刻明白抱怨一點(diǎn)積極意義都沒有,反而會(huì)打亂自己∽秤ǎ現(xiàn)在遇事也會(huì)成熟不少翅帜,更多的是去積極的看待事物。改變自己挺難的命满,但改變自己很重要)涝滴。最初一段時(shí)間,很不情愿的去看這個(gè)項(xiàng)目的先輩們寫下的對(duì)于現(xiàn)在來說不太優(yōu)秀的代碼胶台,然后對(duì)于組長(zhǎng)指派的迭代任務(wù)我也是依樣畫葫蘆的在原有系統(tǒng)上增加修改歼疮。
覺得很多時(shí)候都在和老系統(tǒng)的代碼作斗爭(zhēng),修 bug诈唬,加補(bǔ)丁韩脏,等等。
沒有思考铸磅,當(dāng)然也不會(huì)有沉淀赡矢,更別提進(jìn)步。 我?guī)е环N不屑阅仔、不愿的態(tài)度接手項(xiàng)目吹散,在經(jīng)過一段消極的工作之旅后,我通過閱讀書籍帶給我的思考對(duì)自己審視了一番八酒。
其實(shí)對(duì)于工程師而言空民,一個(gè)很好的練習(xí),就是試著重構(gòu)一個(gè)有很多問題的老系統(tǒng)羞迷。老系統(tǒng)在很多時(shí)候界轩,因?yàn)樵O(shè)計(jì)初期很多需求不一樣,資源和約束也不一樣衔瓮,隨著時(shí)間推移浊猾,才慢慢顯得不足。而當(dāng)你了解一個(gè)系統(tǒng)要做的東西热鞍,以及所有的缺陷和坑与殃,試著去思考:如果是你來重新設(shè)計(jì)這個(gè)系統(tǒng),你會(huì)怎么做碍现?一定會(huì)有哪些選擇?一定會(huì)避免哪些選擇米奸?
這樣的練習(xí)昼接,在腦海里一遍遍地過,即使你沒有時(shí)間和精力去真的重構(gòu)悴晰,也會(huì)對(duì)你能力的提高有很大的幫助慢睡。而有機(jī)會(huì)的時(shí)候逐工,從一些小的地方著手,試圖一點(diǎn)點(diǎn)地在力所能及的范圍內(nèi)改進(jìn)系統(tǒng)漂辐。這比鄙視舊系統(tǒng)泪喊,然后不斷寫出更爛的代碼、把系統(tǒng)變得更糟糕要好很多髓涯。
這段話來自微信公眾號(hào):嘀嗒嘀嗒袒啼,文章題為《為什么有的程序員覺得自己是個(gè)打雜的?》
當(dāng)時(shí)看到這標(biāo)題的時(shí)候總感覺是寫給我的纬纪,不過現(xiàn)在明白醒悟也不晚蚓再,幸運(yùn)如我,不是嗎 :)
嘮嗑結(jié)束了包各,接下來是這次的正文。
需求場(chǎng)景
事情是這樣的,上周五運(yùn)營(yíng)人員提了一個(gè)需求(僅為一個(gè)例子):希望在用戶列表頁面能對(duì)登錄時(shí)間進(jìn)行過濾排序宙项,比如想要看到3月1日至3月2日登錄過的用戶爆存,且根據(jù)這段時(shí)間內(nèi)用戶的登錄時(shí)間進(jìn)行降序排列。結(jié)合數(shù)據(jù)庫(kù)的情況护姆,這個(gè)需求要用到兩張表(一個(gè)用戶表:user矾端,一個(gè)用戶登錄記錄表:user_login_record)。
user 表
user_id | nick |
---|---|
1 | amy |
2 | bob |
user_login_record 表
id | user_id | login_time |
---|---|---|
1 | 1 | 2017-03-01 00:00:00 |
2 | 1 | 2017-03-01 20:00:00 |
3 | 2 | 2017-03-01 05:00:00 |
4 | 2 | 2017-03-01 09:00:00 |
5 | 2 | 2017-03-01 16:00:00 |
心路歷程
如果某個(gè)用戶在過濾的時(shí)間段內(nèi)登錄過兩次及以上签则,那么一定會(huì)遇到需要分組去重的問題须床。
版本1.0
執(zhí)著于使用 DISTINCT 來完成去重的操作,但這樣只能對(duì)某一列進(jìn)行去重渐裂,且無法列出其他列豺旬,也就沒法在一個(gè) SQL 中再進(jìn)行排序操作。
SELECT
DISTINCT user_id
FROM
user_login_record
WHERE
login_time BETWEEN '2017-03-01' AND '2017-03-02'
GROUP BY
user_id
版本2.0
經(jīng)人提醒柒凉,可以使用 聚合函數(shù) 來達(dá)到去重的效果族阅。
SELECT
user_id, MAX(login_time) as max_login_time
FROM
user_login_record
WHERE
login_time BETWEEN '2017-03-01' AND '2017-03-02'
GROUP BY
user_id
ORDER BY
max_login_time DESC
這里拿到了已去重排好序的滿足過濾條件的用戶 ID 以及那個(gè)時(shí)間段內(nèi)最近一次登錄時(shí)間,接下來就是將 user 表中用戶信息獲取到即可
user_id | max_login_time |
---|---|
1 | 2017-03-01 20:00:00 |
2 | 2017-03-01 16:00:00 |
可以通過后端腳本將 user_id 拼接起來作為查詢條件
SELECT
user_id,
nick
FROM
user
WHERE
user_id IN (1, 2)
ORDER BY
FIELD(user_id, 1, 2)
user_id | nick |
---|---|
1 | amy |
2 | bob |
由于兩個(gè)結(jié)果集的順序是一樣的膝捞,因此登錄時(shí)間也是一一對(duì)應(yīng)的坦刀,所以對(duì)其中一個(gè)結(jié)果集循環(huán),對(duì)應(yīng)另一個(gè)結(jié)果集中索引對(duì)應(yīng)的值合并蔬咬。
MySQL 知識(shí)點(diǎn):FIELD
作用:自定義排序
格式
FIELD(value, str1, str2...)
- 場(chǎng)景
由于 MySQL 并不會(huì)按 IN 中傳入的參數(shù)順序輸出結(jié)果集鲤遥,因此需要用到 FIELD 來自定義排序,只需傳入的參數(shù)與 IN 中傳入的參數(shù)完全相同即可
SQL 語句有多重要~
在減少數(shù)據(jù)庫(kù)連接與減少數(shù)據(jù)庫(kù)執(zhí)行負(fù)擔(dān)兩者間進(jìn)行權(quán)衡林艘。運(yùn)用得好可以提升性能盖奈,減少代碼『總之钢坦,多思考究孕。
晚安,各位爹凹,床上那位催著睡覺了厨诸。。禾酱。
首發(fā)于個(gè)人博客 StephenCode
同步在:
知乎專欄:黑白之間
簡(jiǎn)書專題:黑白之間
SegmentFault 專欄:黑白之間
微信公眾號(hào):黒白之間