SQL 藝術:分組去重排序關聯

事情交代

在平常嘮嗑前先簡單說一下本文想要記錄的一次 MySQL 技術回顧:兩張表,對其中一張表進行分組去重後,對結果集進行排序,而後關聯另外一張表獲得最終的結果集程序員

當時推翻重來了幾回,從中也學到很多 SQL 知識,所以有必要寫成文章系統的給本身一個交代。數據庫

OK,接下來先談些感悟。不喜的請略過,謝謝後端

平常嘮嗑

自歷來到這家公司以後,一直在維護後臺系統,其中包括一些舊版頁面的整改、增長新功能等等。熟悉一個已有的業務邏輯較複雜的系統,我我的是沒有什麼經驗的。大二進入老師的實驗室後一直作的是外包項目,每個外包項目都是全新的,都是本身去從頭搭建,因此其中涉及到的代碼我都很是熟悉。微信

所以剛來的時候,拿到這個項目的源代碼,開始去了解熟悉,發現其中用到的語言版本、框架版本等都廣泛偏低,很是不適應,內心也難免有很多抱怨(當我看完李笑來先生的《把時間當作朋友》後,深入明白抱怨一點積極意義都沒有,反而會打亂本身。如今遇事也會成熟很多,更多的是去積極的看待事物。改變本身挺難的,但改變本身很重要)。最初一段時間,很不情願的去看這個項目的先輩們寫下的對於如今來講不太優秀的代碼,而後對於組長指派的迭代任務我也是依樣畫葫蘆的在原有系統上增長修改。框架

以爲不少時候都在和老系統的代碼做鬥爭,修 bug,加補丁,等等。函數

沒有思考,固然也不會有沉澱,更別提進步。 我帶着一種不屑、不肯的態度接手項目,在通過一段消極的工做之旅後,我經過閱讀書籍帶給個人思考對本身審視了一番。性能

其實對於工程師而言,一個很好的練習,就是試着重構一個有不少問題的老系統。老系統在不少時候,由於設計初期不少需求不同,資源和約束也不同,隨着時間推移,才慢慢顯得不足。而當你瞭解一個系統要作的東西,以及全部的缺陷和坑,試着去思考:若是是你來從新設計這個系統,你會怎麼作?必定會有哪些選擇?必定會避免哪些選擇?
這樣的練習,在腦海裏一遍遍地過,即便你沒有時間和精力去真的重構,也會對你能力的提升有很大的幫助。而有機會的時候,從一些小的地方着手,試圖一點點地在力所能及的範圍內改進系統。這比鄙視舊系統,而後不斷寫出更爛的代碼、把系統變得更糟糕要好不少。設計

這段話來自微信公衆號:嘀嗒嘀嗒,文章題爲《爲何有的程序員以爲本身是個打雜的?code

當時看到這標題的時候總感受是寫給個人,不過如今明白醒悟也不晚,幸運如我,不是嗎 :)排序

嘮嗑結束了,接下來是此次的正文。

需求場景

事情是這樣的,上週五運營人員提了一個需求(僅爲一個例子):但願在用戶列表頁面能對登陸時間進行過濾排序,好比想要看到3月1日至3月2日登陸過的用戶,且根據這段時間內用戶的登陸時間進行降序排列。結合數據庫的狀況,這個需求要用到兩張表(一個用戶表:user,一個用戶登陸記錄表: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

心路歷程

若是某個用戶在過濾的時間段內登陸過兩次及以上,那麼必定會遇到須要分組去重的問題。

版本1.0

執着於使用 DISTINCT 來完成去重的操做,但這樣只能對某一列進行去重,且沒法列出其餘列,也就無法在一個 SQL 中再進行排序操做。

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

經人提醒,可使用 聚合函數 來達到去重的效果。

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 以及那個時間段內最近一次登陸時間,接下來就是將 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

因爲兩個結果集的順序是同樣的,所以登陸時間也是一一對應的,因此對其中一個結果集循環,對應另外一個結果集中索引對應的值合併。

MySQL 知識點:FIELD

  • 做用:自定義排序

  • 格式

FIELD(value, str1, str2...)
  • 場景

因爲 MySQL 並不會按 IN 中傳入的參數順序輸出結果集,所以須要用到 FIELD 來自定義排序,只需傳入的參數與 IN 中傳入的參數徹底相同便可

SQL 語句有多重要~

在減小數據庫鏈接與減小數據庫執行負擔二者間進行權衡。運用得好能夠提高性能,減小代碼。總之,多思考。

晚安,各位,牀上那位催着睡覺了。。。

相關文章
相關標籤/搜索