日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

如何写优雅的SQL原生语句?

發(fā)布時間:2025/6/17 数据库 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何写优雅的SQL原生语句? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言:

上一篇講Mysql基本架構時,以"sql查詢語句在MySql架構中具體是怎么執(zhí)行的" 進行了全面的講解。知道了sql查詢語句在MySql架構中的具體執(zhí)行流程,但是為了能夠更好更快的寫出sql語句,我覺得非常有必要知道sql語句中各子句的執(zhí)行順序。看過上一篇文章的小伙伴應該都知道,sql語句最后各子句的執(zhí)行應該是在執(zhí)行器中完成的,存儲引擎對執(zhí)行器提供的數(shù)據(jù)讀寫接口。現(xiàn)在開始我們的學習

語句中各子句完整執(zhí)行順序概括(按照順序號執(zhí)行)

  • from (注:這里也包括from中的子語句)
  • join
  • on
  • where
  • group by(開始使用select中的別名,后面的語句中都可以使用)
  • avg,sum.... 等聚合函數(shù)
  • having
  • select
  • distinct
  • order by
  • limit
  • 每個子句執(zhí)行順序分析

    所有的 查詢語句都是從from開始執(zhí)行的,在執(zhí)行過程中,每個步驟都會為下一個步驟生成一個虛擬表,這個虛擬表將作為下一個執(zhí)行步驟的輸入。

    1. from

    form是一次查詢語句的開端。

    • 如果是一張表,會直接操作這張表;
    • 如果這個from后面是一個子查詢,會先執(zhí)行子查詢中的內容,子查詢的結果也就是第一個虛擬表T1。(注意:子查詢中的執(zhí)行流程也是按照本篇文章講的順序哦)。
    • 如果需要關聯(lián)表,使用join,請看2,3

    2. join

    如果from后面是多張表,join關聯(lián),會首先對前兩個表執(zhí)行一個笛卡爾乘積,這時候就會生成第一個虛擬表T1(注意:這里會選擇相對小的表作為基礎表);

    3. on

    對虛表T1進行ON篩選,只有那些符合的行才會被記錄在虛表T2中。(注意,這里的這里如果還有第三個表與之關聯(lián),會用T2與第三個表進行笛卡爾乘積生產(chǎn)T3表,繼續(xù)重復3. on步驟生成T4表,不過下面的順序講解暫時不針對這里的T3和T4,只是從一個表關聯(lián)查詢T2繼續(xù)說)

    4. where

    對虛擬表T2進行WHERE條件過濾。只有符合的記錄才會被插入到虛擬表T3中。

    5.group by

    group by 子句將中的唯一的值組合成為一組,得到虛擬表T4。如果應用了group by,那么后面的所有步驟都只能操作T4的列或者是執(zhí)行6.聚合函數(shù)(count、sum、avg等)。(注意:原因在于分組后最終的結果集中只包含每個組中的一行。謹記,不然這里會出現(xiàn)很多問題,下面的代碼誤區(qū)會特別說。)

    6. avg,sum.... 等聚合函數(shù)

    聚合函數(shù)只是對分組的結果進行一些處理,拿到某些想要的聚合值,例如求和,統(tǒng)計數(shù)量等,并不生成虛擬表。

    7. having

    應用having篩選器,生成T5。HAVING子句主要和GROUP BY子句配合使用,having篩選器是第一個也是為唯一一個應用到已分組數(shù)據(jù)的篩選器。

    8. select

    執(zhí)行select操作,選擇指定的列,插入到虛擬表T6中。

    9. distinct

    對T6中的記錄進行去重。移除相同的行,產(chǎn)生虛擬表T7.(注意:事實上如果應用了group by子句那么distinct是多余的,原因同樣在于,分組的時候是將列中唯一的值分成一組,同時只為每一組返回一行記錄,那么所以的記錄都將是不相同的。 )

    10. order by

    應用order by子句。按照order_by_condition排序T7,此時返回的一個游標,而不是虛擬表。sql是基于集合的理論的,集合不會預先對他的行排序,它只是成員的邏輯集合,成員的順序是無關緊要的。對表進行排序的查詢可以返回一個對象,這個對象包含特定的物理順序的邏輯組織。這個對象就叫游標。
    oder by的幾點說明

    • 因為order by返回值是游標,那么使用order by 子句查詢不能應用于表表達式。
    • order by排序是很需要成本的,除非你必須要排序,否則最好不要指定order by,
    • order by的兩個參數(shù) asc(升序排列) desc(降序排列)

    11. limit

    取出指定行的記錄,產(chǎn)生虛擬表T9, 并將結果返回。

    limit后面的參數(shù)可以是 一個limit m ,也可以是limit m n,表示從第m條到第n條數(shù)據(jù)。

    (注意:很多開發(fā)人員喜歡使用該語句來解決分頁問題。對于小數(shù)據(jù),使用LIMIT子句沒有任何問題,當數(shù)據(jù)量非常大的時候,使用LIMIT n, m是非常低效的。因為LIMIT的機制是每次都是從頭開始掃描,如果需要從第60萬行開始,讀取3條數(shù)據(jù),就需要先掃描定位到60萬行,然后再進行讀取,而掃描的過程是一個非常低效的過程。所以,對于大數(shù)據(jù)處理時,是非常有必要在應用層建立一定的緩存機制)

    開發(fā)某需求寫的一段sql

    SELECT `userspk`.`avatar` AS `user_avatar`, `a`.`user_id`, `a`.`answer_record`, MAX(`score`) AS `score` FROM (select * from pkrecord order by score desc) as a INNER JOIN `userspk` AS `userspk` ON `a`.`user_id` = `userspk`.`user_id` WHERE `a`.`status` = 1 AND `a`.`user_id` != 'm_6da5d9e0-4629-11e9-b5f7-694ced396953' GROUP BY `user_id` ORDER BY `a`.`score` DESC LIMIT 9; 復制代碼

    查詢結果:

    • 先簡要說一下我要查詢的內容:

    想要查詢pk記錄表中分數(shù)最高的9個用戶記錄和他們的頭像。

    • 通過這段sql實際想一遍sql各字句的執(zhí)行順序

    pk記錄表的數(shù)據(jù)結構設計,每個用戶每天每個館下可能會有多條記錄,所以需要進行分組,并且查詢結果只想拿到每個分組內最高的那條記錄

    這段sql的一些說明:

  • 可能有些同學會認為子查詢沒有必要 直接查詢pk記錄表就可以,但是并不能拿到預期的結果,因為分組后的每個組結果是不進行排序的,而且max拿到的最高分數(shù)肯定是對應的該分組下最高分數(shù),但是其它記錄可能就不是最高分數(shù)對應的那條記錄。所以子查詢非常有必要,它能夠對原始的數(shù)據(jù)首先進行排序,分數(shù)最高的那條就是第一條對應的第一條記錄。
  • 看一下代碼和執(zhí)行結果與帶有子查詢的進行比較,就能理解我上面說的一段話:

    //不使用子查詢 SELECT `userspk`.`avatar` AS `user_avatar`, `pkrecord`.`user_id`, `pkrecord`.`answer_record`, `pkrecord`.`id`, MAX(`score`) AS `score` FROM pkrecord INNER JOIN `userspk` AS `userspk` ON `pkrecord`.`user_id` = `userspk`.`user_id` WHERE `pkrecord`.`status` = 1 AND `pkrecord`.`user_id` != 'm_6da5d9e0-4629-11e9-b5f7-694ced396953' GROUP BY `user_id` ORDER BY `pkrecord`.`score` DESC LIMIT 9; 復制代碼

    查詢結果

    2. 在子查詢中對數(shù)據(jù)已經(jīng)進行排序后,外層排序方式如果和子查詢排序分數(shù)相同,都是分數(shù)倒序,外層的排序可以去掉,沒有必要寫兩遍。

    sql語句中的別名

    別名在哪些情況使用

    在 SQL 語句中,可以為表名稱及字段(列)名稱指定別名

    • 表名稱指定別名

    同時查詢兩張表的數(shù)據(jù)的時候: 未設置別名前:

    SELECT article.title,article.content,user.username FROM article, user 復制代碼

    WHERE article.aid=1 AND article.uid=user.uid 復制代碼

    設置別名后:

    SELECT a.title,a.content,u.username FROM article AS a, user AS u where a.aid=1 and a.uid=u.uid 復制代碼

    好處:使用表別名查詢,可以使 SQL 變得簡潔而更易書寫和閱讀,尤其在 SQL 比較復雜的情況下

    • 查詢字段指定別名

    查詢一張表,直接對查詢字段設置別名

    SELECT username AS name,email FROM user 復制代碼

    查詢兩張表

    好處:字段別名一個明顯的效果是可以自定義查詢數(shù)據(jù)返回的字段名;當兩張表有相同的字段需要都被查詢出,使用別名可以完美的進行區(qū)分,避免沖突

    SELECT a.title AS atitle,u.username,u.title AS utitle FROM article AS a, user AS u where a.uid=u.uid 復制代碼
    • 關聯(lián)查詢時候,關聯(lián)表自身的時候,一些分類表,必須使用別名。

    • 別名也可以在group by與having的時候都可使用

    • 別名可以在order by排序的時候被使用

      查看上面一段sql

    • delete , update MySQL都可以使用別名,別名在多表(級聯(lián))刪除尤為有用

    delete t1,t2 from t_a t1 , t_b t2 where t1.id = t2.id 復制代碼
    • 子查詢結果需要使用別名

      查看上面一段sql

    別名使用注意事項

    • 雖然定義字段別名的 AS 關鍵字可以省略,但是在使用別名時候,建議不要省略 AS 關鍵字

    書寫sql語句的注意事項

    書寫規(guī)范上的注意

    • 字符串類型的要加單引號
    • select后面的每個字段要用逗號分隔,但是最后連著from的字段不要加逗號
    • 使用子查詢創(chuàng)建臨時表的時候要使用別名,否則會報錯。

    為了增強性能的注意

    • 不要使用“select * from ……”返回所有列,只檢索需要的列,可避免后續(xù)因表結構變化導致的不必要的程序修改,還可降低額外消耗的資源
    • 不要檢索已知的列
    select user_id,name from User where user_id = ‘10000050復制代碼
    • 使用可參數(shù)化的搜索條件,如=, >, >=, <, <=, between, in, is null以及l(fā)ike ‘%’;盡量不要使用非參數(shù)化的負向查詢,這將導致無法使用索引,如<>, !=, !>, !<, not in, not like, not exists, not between, is not null, like ‘%’
    • 當需要驗證是否有符合條件的記錄時,使用exists,不要使用count(*),前者在第一個匹配記錄處返回,后者需要遍歷所有匹配記錄
    • Where子句中列的順序與需使用的索引順序保持一致,不是所有數(shù)據(jù)庫的優(yōu)化器都能對此順序進行優(yōu)化,保持良好編程習慣(索引相關)
    • 不要在where子句中對字段進行運算或函數(shù)(索引相關)
  • 如where amount / 2 > 100,即使amount字段有索引,也無法使用,改成where amount > 100 * 2就可使用amount列上的索引
  • 如where substring( Lastname, 1, 1) = ‘F’就無法使用Lastname列上的索引,而where Lastname like ‘F%’或者where Lastname >= ‘F’ and Lastname < ‘G’就可以
    • 在有min、max、distinct、order by、group by操作的列上建索引,避免額外的排序開銷(索引相關)

    • 小心使用or操作,and操作中任何一個子句可使用索引都會提高查詢性能,但是or條件中任何一個不能使用索引,都將導致查詢性能下降,如where member_no = 1 or provider_no = 1,在member_no或provider_no任何一個字段上沒有索引,都將導致表掃描或聚簇索引掃描(索引相關)

    • Between一般比in/or高效得多,如果能在between和in/or條件中選擇,那么始終選擇between條件,并用>=和<=條件組合替代between子句,因為不是所有數(shù)據(jù)庫的優(yōu)化器都能把between子句改寫為>=和<=條件組合,如果不能改寫將導致無法使用索引(索引相關)

    • 調整join操作順序以使性能最優(yōu),join操作是自頂向下的,盡量把結果集小的兩個表關聯(lián)放在前面,可提高性能。(join相關) 注意:索引和關聯(lián)我會單獨拿出來兩篇文章進行詳細講解,在這個注意事項中只是簡單提一下。

    力推文章:

    Mysql基礎架構你不知道的那些事!

    兩篇文章一起學習能徹底搞懂sql語句到底怎么在架構中執(zhí)行的,到底應該怎么寫優(yōu)秀的sql。

    覺得本文對你有幫助?請分享給更多人

    歡迎大家關注我的公眾號——程序員成長指北。請自行微信搜索——“程序員成長指北”

    總結

    以上是生活随笔為你收集整理的如何写优雅的SQL原生语句?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。