<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          養(yǎng)成這些 SQL 習慣 是一筆財富!

          共 5792字,需瀏覽 12分鐘

           ·

          2021-12-24 20:25

          以下內容來自公眾號逆鋒起筆,關注每日干貨及時送達

          來源:數(shù)據(jù)前線
          我們做軟件開發(fā)的,大部分人都離不開跟數(shù)據(jù)庫打交道,特別是ERP開發(fā)的,跟數(shù)據(jù)庫打交道更是頻繁,存儲過程動不動就是上千行,如果數(shù)據(jù)量大,人員流動大,那么我么還能保證下一段時間系統(tǒng)還能流暢的運行嗎?我么還能保證下一個人能看懂我么的存儲過程嗎?那么我結合公司平時的培訓和平時個人工作經驗和大家分享一下,希望對大家有幫助。
          要知道SQL語句,我想我們有必要知道SQLServer查詢分析器怎么執(zhí)行我么SQL語句的,我么很多人會看執(zhí)行計劃,或者用Profile來監(jiān)視和調優(yōu)查詢語句或者存儲過程慢的原因,但是如果我們知道查詢分析器的執(zhí)行邏輯順序,下手的時候就胸有成竹,那么下手是不是有把握點呢?

          ?一、查詢的邏輯執(zhí)行順序
          ?(1) FROM < left_table>?
          ?(3) < join_type>? JOIN < right_table>?? (2) ON < join_condition>?
          ?(4) WHERE < where_condition>?
          ?(5) GROUP BY < group_by_list>?
          ?(6) WITH {cube | rollup}
          ?(7) HAVING < having_condition>?
          ?(8) SELECT??
          (9) DISTINCT (11) < top_specification>? < select_list>?
          ?(10) ORDER BY < order_by_list>?

          標準的SQL 的解析順序為:
          ?(1)FROM 子句 組裝來自不同數(shù)據(jù)源的數(shù)據(jù)
          ?(2)WHERE 子句 基于指定的條件對記錄進行篩選
          ?(3)GROUP BY 子句 將數(shù)據(jù)劃分為多個分組
          ?(4)使用聚合函數(shù)進行計算
          ?(5)使用HAVING子句篩選分組
          ?(6)計算所有的表達式
          ?(7)使用ORDER BY對結果集進行排序

          二、執(zhí)行順序
          1.FROM:對FROM子句中前兩個表執(zhí)行笛卡爾積生成虛擬表vt1
          2.ON:對vt1表應用ON篩選器只有滿足< join_condition> 為真的行才被插入vt2
          3.OUTER(join):如果指定了 OUTER JOIN保留表(preserved table)中未找到的行將行作為外部行添加到vt2 生成t3如果from包含兩個以上表則對上一個聯(lián)結生成的結果表和下一個表重復執(zhí)行步驟和步驟直接結束
          4.WHERE:對vt3應用 WHERE 篩選器只有使< where_condition> 為true的行才被插入vt4
          5.GROUP BY:按GROUP BY子句中的列列表對vt4中的行分組生成vt5
          6.CUBE|ROLLUP:把超組(supergroups)插入vt6 生成vt6
          7.HAVING:對vt6應用HAVING篩選器只有使< having_condition> 為true的組才插入vt7
          8.SELECT:處理select列表產生vt8
          9.DISTINCT:將重復的行從vt8中去除產生vt9
          10.ORDER BY:將vt9的行按order by子句中的列列表排序生成一個游標vc10
          11.TOP:從vc10的開始處選擇指定數(shù)量或比例的行生成vt11 并返回調用者
          看到這里,那么用過linqtosql的語法有點相似啊?如果我們我們了解了sqlserver執(zhí)行順序,那么我們就接下來進一步養(yǎng)成日常sql好習慣,也就是在實現(xiàn)功能同時有考慮性能的思想,數(shù)據(jù)庫是能進行集合運算的工具,我們應該盡量的利用這個工具,所謂集合運算實際就是批量運算,就是盡量減少在客戶端進行大數(shù)據(jù)量的循環(huán)操作,而用SQL語句或者存儲過程代替。

          三、只返回需要的數(shù)據(jù)
          返回數(shù)據(jù)到客戶端至少需要數(shù)據(jù)庫提取數(shù)據(jù)、網絡傳輸數(shù)據(jù)、客戶端接收數(shù)據(jù)以及客戶端處理數(shù)據(jù)等環(huán)節(jié),如果返回不需要的數(shù)據(jù),就會增加服務器、網絡和客戶端的無效勞動,其害處是顯而易見的,避免這類事件需要注意:
          A、橫向來看
          (1)不要寫SELECT *的語句,而是選擇你需要的字段。
          (2)當在SQL語句中連接多個表時, 請使用表的別名并把別名前綴于每個Column上.這樣一來,就可以減少解析的時間并減少那些由Column歧義引起的語法錯誤。
          --如有表table1(ID,col1)和table2 (ID,col2)
          Select?A.ID, A.col1, B.col2
          -- Select A.ID, col1, col2 –不要這么寫,不利于將來程序擴展
          from?table1 A?inner?join?table2 B?on?A.ID=B.ID?Where?…

          ?B、縱向來看?
          ?(1)合理寫WHERE子句,不要寫沒有WHERE的SQL語句。?
          ?(2) SELECT TOP N * --沒有WHERE條件的用此替代?

          四 、盡量少做重復的工作
          A、控制同一語句的多次執(zhí)行,特別是一些基礎數(shù)據(jù)的多次執(zhí)行是很多程序員很少注意的。
          B、減少多次的數(shù)據(jù)轉換,也許需要數(shù)據(jù)轉換是設計的問題,但是減少次數(shù)是程序員可以做到的。
          C、杜絕不必要的子查詢和連接表,子查詢在執(zhí)行計劃一般解釋成外連接,多余的連接表帶來額外的開銷。
          D、合并對同一表同一條件的多次UPDATE,比如
          UPDATE?EMPLOYEE?SET?FNAME='HAIWER'
          WHERE?EMP_ID=' VPA30890F'
          UPDATE?EMPLOYEE?SET?LNAME='YANG'
          WHERE?EMP_ID=' VPA30890F'
          這兩個語句應該合并成以下一個語句
          UPDATE?EMPLOYEE?SET?FNAME='HAIWER',LNAME='YANG'
          WHERE?EMP_ID=' VPA30890F'
          E、UPDATE操作不要拆成DELETE操作+INSERT操作的形式,雖然功能相同,但是性能差別是很大的。

          五、注意臨時表和表變量的用法
          在復雜系統(tǒng)中,臨時表和表變量很難避免,關于臨時表和表變量的用法,需要注意:
          A、如果語句很復雜,連接太多,可以考慮用臨時表和表變量分步完成。
          B、如果需要多次用到一個大表的同一部分數(shù)據(jù),考慮用臨時表和表變量暫存這部分數(shù)據(jù)。
          C、如果需要綜合多個表的數(shù)據(jù),形成一個結果,可以考慮用臨時表和表變量分步匯總這多個表的數(shù)據(jù)。
          D、其他情況下,應該控制臨時表和表變量的使用。
          E、關于臨時表和表變量的選擇,很多說法是表變量在內存,速度快,應該首選表變量,但是在實際使用中發(fā)現(xiàn),
          (1)主要考慮需要放在臨時表的數(shù)據(jù)量,在數(shù)據(jù)量較多的情況下,臨時表的速度反而更快。
          (2)執(zhí)行時間段與預計執(zhí)行時間(多長)
          F、關于臨時表產生使用SELECT INTO和CREATE TABLE + INSERT INTO的選擇,一般情況下,
          SELECT INTO會比CREATE TABLE + INSERT INTO的方法快很多,
          但是SELECT INTO會鎖定TEMPDB的系統(tǒng)表SYSOBJECTS、SYSINDEXES、SYSCOLUMNS,在多用戶并發(fā)環(huán)境下,容易阻塞其他進程,
          所以我的建議是,在并發(fā)系統(tǒng)中,盡量使用CREATE TABLE + INSERT INTO,而大數(shù)據(jù)量的單個語句使用中,使用SELECT INTO。

          六、子查詢的用法
          子查詢是一個 SELECT 查詢,它嵌套在 SELECT、INSERT、UPDATE、DELETE 語句或其它子查詢中。
          任何允許使用表達式的地方都可以使用子查詢,子查詢可以使我們的編程靈活多樣,可以用來實現(xiàn)一些特殊的功能。但是在性能上,
          往往一個不合適的子查詢用法會形成一個性能瓶頸。如果子查詢的條件中使用了其外層的表的字段,這種子查詢就叫作相關子查詢。
          相關子查詢可以用IN、NOT IN、EXISTS、NOT EXISTS引入。關于相關子查詢,應該注意:
          (1)
          A、NOT IN、NOT EXISTS的相關子查詢可以改用LEFT JOIN代替寫法。比如:?
          SELECT?PUB_NAME?FROM?PUBLISHERS
          WHERE?PUB_ID?NOT?IN
          (SELECT?PUB_ID?FROM?TITLES
          WHERE?TYPE?=?'BUSINESS')
          --可以改寫成:
          SELECT?A.PUB_NAME?FROM?PUBLISHERS A
          LEFT?JOIN?TITLES B?ON?B.TYPE =?'BUSINESS'?AND?A.PUB_ID=B. PUB_ID
          WHERE?B.PUB_ID?IS?NULL

          (2)
          SELECT?TITLE?FROM?TITLES
          WHERE?NOT?EXISTS
          (SELECT?TITLE_ID?FROM?SALES
          WHERE?TITLE_ID = TITLES.TITLE_ID)
          可以改寫成:
          SELECT?TITLE
          FROM?TITLES
          LEFT?JOIN?SALES?ON?SALES.TITLE_ID = TITLES.TITLE_ID
          WHERE?SALES.TITLE_ID?IS?NULL

          B、 如果保證子查詢沒有重復 ,IN、EXISTS的相關子查詢可以用INNER JOIN 代替。比如:
          SELECT?PUB_NAME?FROM?PUBLISHERS
          WHERE?PUB_ID?IN
          (SELECT?PUB_ID?FROM?TITLES
          WHERE?TYPE?=?'BUSINESS')
          可以改寫成:
          SELECT?A.PUB_NAME?--SELECT DISTINCT A.PUB_NAME
          FROM?PUBLISHERS A
          INNER?JOIN?TITLES B
          ON??B.TYPE =?'BUSINESS'?AND?A.PUB_ID=B. PUB_ID

          (3)
          C、 IN的相關子查詢用EXISTS代替,比如
          SELECT?PUB_NAME?FROM?PUBLISHERS
          WHERE?PUB_ID?IN
          (SELECT?PUB_ID?FROM?TITLES?WHERE?TYPE?=?'BUSINESS')
          --可以用下面語句代替:
          SELECT?PUB_NAME?FROM?PUBLISHERS
          WHERE?EXISTS
          (SELECT?1?FROM?TITLES
          WHERE?TYPE?=?'BUSINESS'?AND?PUB_ID= PUBLISHERS.PUB_ID)

          D、不要用COUNT(*)的子查詢判斷是否存在記錄,最好用LEFT?JOIN或者EXISTS,比如有人寫這樣的語句:
          SELECT?JOB_DESC?FROM?JOBS
          WHERE?(SELECT?COUNT(*)?FROM?EMPLOYEE
          WHERE?JOB_ID=JOBS.JOB_ID)=0
          --應該改成:
          SELECT?JOBS.JOB_DESC?FROM?JOBS
          LEFT?JOIN?EMPLOYEE?ON?EMPLOYEE.JOB_ID=JOBS.JOB_ID
          WHERE?EMPLOYEE.EMP_ID?IS?NULL
          ?
          SELECT?JOB_DESC?FROM?JOBS
          WHERE?(SELECT?COUNT(*)?FROM?EMPLOYEE
          WHERE?JOB_ID=JOBS.JOB_ID)<>0
          --應該改成:
          SELECT?JOB_DESC?FROM?JOBS
          WHERE?EXISTS?(SELECT?1?FROM?EMPLOYEE?WHERE?JOB_ID=JOBS.JOB_ID)


          七、盡量使用索引
          建立索引后,并不是每個查詢都會使用索引,在使用索引的情況下,索引的使用效率也會有很大的差別。只要我們在查詢語句中沒有強制指定索引,
          索引的選擇和使用方法是SQLSERVER的優(yōu)化器自動作的選擇,而它選擇的根據(jù)是查詢語句的條件以及相關表的統(tǒng)計信息,這就要求我們在寫SQL
          語句的時候盡量使得優(yōu)化器可以使用索引。為了使得優(yōu)化器能高效使用索引,寫語句的時候應該注意:
          (1)
          A、不要對索引字段進行運算,而要想辦法做變換,比如
          SELECT?ID?FROM?T?WHERE?NUM/2=100
          應改為:
          SELECT?ID?FROM?T?WHERE?NUM=100*2

          SELECT?ID?FROM?T?WHERE?NUM/2=NUM1
          如果NUM有索引應改為:
          SELECT?ID?FROM?T?WHERE?NUM=NUM1*2
          如果NUM1有索引則不應該改。
          (2)
          發(fā)現(xiàn)過這樣的語句:
          SELECT?年,月,金額?FROM?結余表 ?WHERE?100*年+月=2010*100+10
          應該改為:
          SELECT?年,月,金額?FROM?結余表?WHERE?年=2010?AND月=10
          B、 不要對索引字段進行格式轉換
          日期字段的例子:
          WHERE CONVERT(VARCHAR(10), 日期字段,120)='2010-07-15'
          應該改為
          WHERE日期字段〉='2010-07-15'???AND?? 日期字段<'2010-07-16'

          ISNULL轉換的例子:
          WHERE ISNULL(字段,'')<>''應改為:WHERE字段<>''
          WHERE ISNULL(字段,'')=''不應修改
          WHERE ISNULL(字段,'F') ='T'應改為: WHERE字段='T'
          WHERE ISNULL(字段,'F')<>'T'不應修改
          (3)
          C、 不要對索引字段使用函數(shù)
          WHERE?LEFT(NAME,?3)='ABC'?或者?WHERE?SUBSTRING(NAME,1,?3)='ABC'
          應改為: WHERE NAME LIKE?'ABC%'
          日期查詢的例子:
          WHERE?DATEDIFF(DAY, 日期,'2010-06-30')=0
          應改為:WHERE 日期>='2010-06-30'?AND 日期 <'2010-07-01'
          WHERE?DATEDIFF(DAY, 日期,'2010-06-30')>0
          應改為:WHERE 日期 <'2010-06-30'
          WHERE?DATEDIFF(DAY, 日期,'2010-06-30')>
          =0
          應改為:WHERE 日期 <'2010-07-01'
          WHERE?DATEDIFF(DAY, 日期,'2010-06-30')<0
          應改為:WHERE 日期>
          ='2010-07-01'
          WHERE?DATEDIFF(DAY, 日期,'2010-06-30')<=0
          應改為:WHERE 日期>='2010-06-30'
          D、不要對索引字段進行多字段連接
          比如:
          WHERE FAME+?'. '+LNAME='HAIWEI.YANG'
          應改為:
          WHERE FNAME='HAIWEI'?AND?LNAME='YANG'
          ??
          八、多表連接的連接條件對索引的選擇有著重要的意義,所以我們在寫連接條件條件的時候需要特別注意。
          ? A、多表連接的時候,連接條件必須寫全,寧可重復,不要缺漏。
          ? B、連接條件盡量使用聚集索引
          ? C、注意ON、WHERE和HAVING部分條件的區(qū)別
          ? ON是最先執(zhí)行, WHERE次之,HAVING最后,因為ON是先把不符合條件的記錄過濾后才進行統(tǒng)計,它就可以減少中間運算要處理的數(shù)據(jù),按理說應該速度是最快的,WHERE也應該比 HAVING快點的,因為它過濾數(shù)據(jù)后才進行SUM,在兩個表聯(lián)接時才用ON的,所以在一個表的時候,就剩下WHERE跟HAVING比較了
          考慮聯(lián)接優(yōu)先順序:
          (1)INNER?JOIN
          (2)LEFT JOIN (注:RIGHT JOIN 用 LEFT JOIN 替代)
          (3)CROSS?JOIN
          其它注意和了解的地方有:
          A、在IN后面值的列表中,將出現(xiàn)最頻繁的值放在最前面,出現(xiàn)得最少的放在最后面,減少判斷的次數(shù)
          B、注意UNION和UNION ALL的區(qū)別。--允許重復數(shù)據(jù)用UNION ALL好??
          C、注意使用DISTINCT,在沒有必要時不要用
          ?D、TRUNCATE TABLE 與 DELETE 區(qū)別
          ?E、減少訪問數(shù)據(jù)庫的次數(shù)
          還有就是我們寫存儲過程,如果比較長的話,最后用標記符標開,因為這樣可讀性很好,即使語句寫的不怎么樣但是語句工整,C# 有region
          sql我比較喜歡用的就是
          --startof? 查詢在職人數(shù)
          ?????sql語句
          ??--end of

          正式機器上我們一般不能隨便調試程序,但是很多時候程序在我們本機上沒問題,但是進正式系統(tǒng)就有問題,但是我們又不能隨便在正式機器上操作,那么怎么辦呢?我們可以用回滾來調試我們的存儲過程或者是sql語句,從而排錯。
          BEGIN?TRAN
          UPDATE?a?SET?字段=''
          ROLLBACK? ? ? ?

          作業(yè)存儲過程我一般會加上下面這段,這樣檢查錯誤可以放在存儲過程,如果執(zhí)行錯誤回滾操作,但是如果程序里面已經有了事務回滾,那么存儲過程就不要寫事務了,這樣會導致事務回滾嵌套降低執(zhí)行效率,但是我們很多時候可以把檢查放在存儲過程里,這樣有利于我們解讀這個存儲過程,和排錯。
          BEGIN?TRANSACTION??
          --事務回滾開始
          --檢查報錯
          IF?( @@ERROR?>?0?) ? ?
          ? ??BEGIN?? ? ?
          --回滾操作
          ? ? ??ROLLBACK?TRANSACTION?? ? ?
          ? ? ? RAISERROR('刪除工作報告錯誤',?16,?3) ? ? ? ?
          ? ? ??RETURN?? ? ? ?
          ? ??END??
          --結束事務
          ?COMMIT?TRANSACTION

          逆鋒起筆專注于程序員圈子,你不但可以學習到java、python等主流技術干貨,還可以第一時間獲悉最新技術動態(tài)、內測資格BAT大佬的經驗、精品視頻教程副業(yè)賺錢經驗,微信搜索readdot關注!

          圖解 SQL 基礎知識
          SQL 中去重的三種方法
          MySQL 8.0 可以操作 JSON 了?。?/span>
          SQL 中如何給指定數(shù)據(jù)庫創(chuàng)建只讀用戶?
          MySQL 大批量插入,如何過濾掉重復數(shù)據(jù)?


          優(yōu)秀的讀者“點贊”傳統(tǒng)美德?
          瀏覽 32
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  西西4444www无码大胆 | 国产精品久久久九九性 | 日韩免费无码 | 人人操超碰在线观看 | 亚洲A∨网站 |