讀SQL進階教程筆記16_SQL優化讓SQL飛起來

来源:https://www.cnblogs.com/lying7/archive/2023/05/15/17380168.html
-Advertisement-
Play Games

1. 查詢速度慢並不只是因為SQL語句本身,還可能是因為記憶體分配不佳、文件結構不合理等其他原因 1.1. 都是為了減少對硬碟的訪問 2. 不同代碼能夠得出相同結果 2.1. 從理論上來說,得到相同結果的不同代碼應該有相同的性能 2.2. 遺憾的是,查詢優化器生成的執行計劃很大程度上要受到代碼外部結構 ...


1. 查詢速度慢並不只是因為SQL語句本身,還可能是因為記憶體分配不佳、文件結構不合理等其他原因

1.1. 都是為了減少對硬碟的訪問

2. 不同代碼能夠得出相同結果

2.1. 從理論上來說,得到相同結果的不同代碼應該有相同的性能

2.2. 遺憾的是,查詢優化器生成的執行計劃很大程度上要受到代碼外部結構的影響

2.3. 如果想優化查詢性能,必須知道如何寫代碼才能使優化器的執行效率更高

3. 使用高效的查詢

3.1. 參數是子查詢時,使用EXISTS代替IN

3.1.1. IN謂詞卻有成為性能優化的瓶頸的危險

3.1.1.1. 當IN的參數是子查詢時,資料庫首先會執行子查詢,然後將結果存儲在一張臨時的工作表裡(內聯視圖),然後掃描整個視圖

3.1.1.2. 從代碼的可讀性上來看,IN要比EXISTS好

3.1.2. 示例

3.1.2.1.

  --慢

    SELECT *
      FROM Class_A
     WHERE id IN (SELECT id
                    FROM Class_B);
    --快
    SELECT *
      FROM Class_A  A
     WHERE EXISTS
            (SELECT *
              FROM Class_B  B
              WHERE A.id = B.id);

3.1.2.1.1. 如果連接列(id)上建立了索引,那麼查詢Class_B時不用查實際的表,只需查索引就可以了
3.1.2.1.2. 如果使用EXISTS,那麼只要查到一行數據滿足條件就會終止查詢,不用像使用IN時一樣掃描全表
3.1.2.1.2.1. 在這一點上NOT EXISTS也一樣
3.1.2.1.3. 使用EXISTS的話,資料庫不會生成臨時的工作表

3.2. 參數是子查詢時,使用連接代替IN

3.2.1. 示例

3.2.1.1. --使用連接代替IN

    SELECT A.id, A.name
      FROM Class_A A INNER JOIN Class_B B
        ON A.id = B.id;
3.2.1.1.1. 至少能用到一張表的“id”列上的索引
3.2.1.1.2. 沒有了子查詢,所以資料庫也不會生成中間表
3.2.1.1.3. 如果沒有索引,那麼與連接相比,可能EXISTS會略勝一籌

4. 避免排序

4.1. 在SQL語言中,用戶不能顯式地命令資料庫進行排序操作

4.2. 對用戶隱藏這樣的操作正是SQL的設計思想

4.3. 在資料庫內部頻繁地進行著暗中的排序

4.3.1. 會進行排序的代表性的運算

4.3.1.1. GROUP BY子句

4.3.1.2. ORDER BY子句

4.3.1.3. 聚合函數(SUM、COUNT、AVG、MAX、MIN)

4.3.1.4. DISTINCT

4.3.1.5. 集合運算符(UNION、INTERSECT、EXCEPT)

4.3.1.6. 視窗函數(RANK、ROW_NUMBER等)

4.4. 靈活使用集合運算符的ALL可選項

4.4.1. 如果不在乎結果中是否有重覆數據,或者事先知道不會有重覆數據,請使用UNION ALL代替UNION

4.4.2. 加上ALL可選項是優化性能的一個非常有效的手段

4.4.3. 對於INTERSECT和EXCEPT也是一樣的,加上ALL可選項後就不會進行排序了

4.5. 使用EXISTS代替DISTINCT

4.5.1. 為了排除重覆數據,DISTINCT也會進行排序

4.5.1.1.

 SELECT I.item_no

      FROM Items I INNER JOIN SalesHistory SH
        ON I. item_no = SH. item_no;

4.5.1.2.

  SELECT DISTINCT I.item_no

      FROM Items I INNER JOIN SalesHistory SH
        ON I. item_no = SH. item_no;

4.5.1.3.

SELECT item_no

      FROM Items I
     WHERE EXISTS
              (SELECT *
                  FROM SalesHistory SH
                WHERE I.item_no = SH.item_no);

4.6. 在極值函數中使用索引(MAX/MIN)

4.6.1. 使用這兩個函數時都會進行排序

4.6.1.1. --這樣寫需要掃描全表

    SELECT MAX(item)
      FROM Items;

4.6.2. 如果參數欄位上建有索引,則只需要掃描索引,不需要掃描整張表

4.6.2.1. --這樣寫能用到索引

    SELECT MAX(item_no)
      FROM Items;

4.6.3. 對於聯合索引,只要查詢條件是聯合索引的第一個欄位,索引就是有效的

4.6.4. 這種方法並不是去掉了排序這一過程,而是優化了排序前的查找速度,從而減弱排序對整體性能的影響

4.7. 能寫在WHERE子句里的條件不要寫在HAVING子句里

4.7.1. --聚合後使用HAVING子句過濾

    SELECT sale_date, SUM(quantity)
      FROM SalesHistory
     GROUP BY sale_date
    HAVING sale_date = '2007-10-01';

4.7.2. --聚合前使用WHERE子句過濾

    SELECT sale_date, SUM(quantity)
      FROM SalesHistory
     WHERE sale_date = '2007-10-01'
     GROUP BY sale_date;

4.7.2.1. 在使用GROUP BY子句聚合時會進行排序,如果事先通過WHERE子句篩選出一部分行,就能夠減輕排序的負擔

4.7.2.2. 第二個是在WHERE子句的條件里可以使用索引。HAVING子句是針對聚合後生成的視圖進行篩選的,但是很多時候聚合後的視圖都沒有繼承原表的索引結構

4.8. 在GROUP BY子句和ORDER BY子句中使用索引

4.8.1. 通過指定帶索引的列作為GROUP BY和ORDER BY的列,可以實現高速查詢

4.8.2. 在一些資料庫中,如果操作對象的列上建立的是唯一索引,那麼排序過程本身都會被省略掉

5. 真正用到索引!

5.1. 在索引欄位上進行運算

5.1.1.

SELECT *

      FROM SomeTable
     WHERE col_1 * 1.1 > 100;

5.2. 把運算的表達式放到查詢條件的右側,就能用到索引了

5.2.1. WHERE col_1 > 100 / 1.1

5.3. 在查詢條件的左側使用函數時,也不能用到索引

5.3.1.

SELECT *

      FROM SomeTable
     WHERE SUBSTR(col_1, 1, 1) = 'a';

5.4. 如果無法避免在左側進行運算,那麼使用函數索引也是一種辦法

5.5. 使用索引時,條件表達式的左側應該是原始欄位

5.6. 使用IS NULL謂詞

5.6.1. 索引欄位是不存在NULL的,所以指定IS NULL和IS NOT NULL的話會使得索引無法使用,進而導致查詢性能低下

5.6.1.1.

SELECT *

      FROM  SomeTable
     WHERE  col_1 IS NULL;

5.6.1.2. --IS NOT NULL的代替方案

    SELECT *
      FROM  SomeTable
     WHERE  col_1 > 0;
5.6.1.2.1. 如果要選擇“非NULL的行”,正確的做法還是使用IS NOT NULL

5.7. 使用否定形式

5.7.1. 否定形式不能用到索引

5.7.1.1. <>

5.7.1.2. ! =

5.7.1.3. NOT IN

5.8. 使用OR

5.8.1. 在col_1和col_2上分別建立了不同的索引,或者建立了(col_1, col_2)這樣的聯合索引時,如果使用OR連接條件,那麼要麼用不到索引,要麼用到了但是效率比AND要差很多

5.8.2. 如果無論如何都要使用OR,那麼有一種辦法是點陣圖索引。但是這種索引的話更新數據時的性能開銷會增大

5.9. 使用聯合索引時,列的順序錯誤

5.9.1. 假設存在這樣順序的一個聯合索引“col_1, col_2, col_3”

5.9.2.

 ○   SELECT * FROM SomeTable WHERE col_1 = 10 AND col_2 = 100 AND col_3 = 500;

    ○   SELECT * FROM SomeTable WHERE col_1 = 10 AND col_2 = 100 ;
    ×   SELECT * FROM SomeTable WHERE col_1 = 10 AND col_3 = 500 ;
    ×   SELECT * FROM SomeTable WHERE col_2 = 100 AND col_3 = 500 ;
    ×   SELECT * FROM SomeTable WHERE col_2 = 100 AND col_1 = 10 ;

5.9.3. 聯合索引中的第一列(col_1)必須寫在查詢條件的開頭,而且索引中列的順序不能顛倒

5.9.4. 有些資料庫里順序顛倒後也能使用索引,但是性能還是比順序正確時差一些

5.9.5. 如果無法保證查詢條件里列的順序與索引一致,可以考慮將聯合索引拆分為多個索引

5.10. 使用LIKE謂詞進行後方一致或中間一致的匹配

5.10.1. 只有前方一致的匹配才能用到索引

5.10.2.

 ×   SELECT  *   FROM  SomeTable  WHERE  col_1  LIKE '%a';

    ×   SELECT  *   FROM  SomeTable  WHERE  col_1  LIKE '%a%';
    ○   SELECT  *   FROM  SomeTable  WHERE  col_1  LIKE 'a%';

5.11. 進行預設的類型轉換

5.11.1. 預設的類型轉換不僅會增加額外的性能開銷,還會導致索引不可用

5.11.2. 在需要類型轉換時顯式地進行類型轉換

6. 減少中間表

6.1. 子查詢的結果會被看成一張新表,這張新表與原始表一樣,可以通過代碼進行操作

6.2. 靈活使用HAVING子句

6.2.1. 對聚合結果指定篩選條件時不需要專門生成中間表

6.2.2.

 SELECT sale_date, MAX(quantity)

      FROM SalesHistory
     GROUP BY sale_date
    HAVING MAX(quantity) >= 10;

6.2.3. HAVING子句和聚合操作是同時執行的,所以比起生成中間表後再執行的WHERE子句,效率會更高一些,而且代碼看起來也更簡潔

6.3. 需要對多個欄位使用IN謂詞時,將它們彙總到一處

6.3.1.

SELECT id, state, city

      FROM Addresses1 A1
     WHERE state IN (SELECT state
                      FROM Addresses2 A2
                      WHERE A1.id = A2.id)
      AND city  IN (SELECT city
                      FROM Addresses2 A2
                      WHERE A1.id = A2.id);

6.3.2.

SELECT *

      FROM Addresses1 A1
     WHERE id || state || city
        IN (SELECT id || state|| city
              FROM Addresses2 A2);

6.3.2.1. 子查詢不用考慮關聯性,而且只執行一次就可以

6.3.3.

SELECT *

      FROM Addresses1 A1
     WHERE (id, state, city)
        IN (SELECT id, state, city
              FROM Addresses2 A2);

6.3.3.1. 不用擔心連接欄位時出現的類型轉換問題

6.3.3.2. 不會對欄位進行加工,因此可以使用索引

6.4. 先進行連接再進行聚合

6.4.1. 連接和聚合同時使用時,先進行連接操作可以避免產生中間表

6.4.1.1. 連接做的是“乘法運算”

6.4.1.2. 連接表雙方是一對一、一對多的關係時,連接運算後數據的行數不會增加

6.5. 合理地使用視圖

6.5.1. 特別是視圖的定義語句中包含以下運算的時候,SQL會非常低效,執行速度也會變得非常慢

6.5.1.1. 聚合函數(AVG、COUNT、SUM、MIN、MAX)

6.5.1.2. 集合運算符(UNION、INTERSECT、EXCEPT等)

6.5.2. 物化視圖(materialized view)等技術。當視圖的定義變得複雜時,可以考慮使用一下


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、概述 PUT 和 PATCH 方法用於更新現有資源。 它們之間的區別是,PUT 會替換整個資源,而 PATCH 僅指定更改。 在 ASP.NET Core Web API 中,由於 C# 是一種靜態語言(dynamic 在此不表),當我們定義了一個類型用於接收 HTTP Patch 請求參數的時 ...
  • 前言 之前有個項目需要執行一個略微耗時的操作大概五六七八九十秒這樣子,這個時候程式不能做其他操作,只能等待操作完成。為了提升一絲使用體驗同時讓Winform程式看上去高級一點🎃🎃🎃,就想到加一個遮罩層(MaskLayer)。雖然Winform沒有原生的遮罩層,但是實現起來也不是很麻煩。 遮罩層 ...
  • 目錄 沁恆 CH32V208(一): CH32V208WBU6 評估板上手報告和Win10環境配置 沁恆 CH32V208(二): CH32V208的儲存結構, 啟動模式和時鐘 沁恆 CH32V208(三): CH32V208 Ubuntu22.04 Makefile VSCode環境配置 沁恆 C ...
  • 以 redhat7.4為例,網上的解決方案多是針對ubuntu的,需要進入ubuntu的預覽系統,redhat好像沒這個東西 問題:新添磁碟後開機無法進入系統。 似乎是因為電腦將新增的空硬碟作為了系統盤進行啟動,所以無法啟動系統。 解決方案:只要讓電腦將裝有linux系統的硬碟進行啟動就可以解決 ...
  • 切換 Windows 的系統語言 Windows 10 專業版 (1)點擊左下角開始菜單欄 --> 設置 --> 時間和語言 --> 語言。 (2)點擊添加語言,在彈出的列表框中,選擇你要安裝的語言。 (3)下載完語言包後,點擊 Windows 顯示語言下拉框,選擇剛剛安裝的語言。 (4)選擇新的語 ...
  • 定時器詳解 引出 定時器是一個比較常見的數據結構,或者說框架,以一個最簡單的例子引出,在游戲中,冷卻時間使用的就是定時器; 所以說定時器是**等待時間過期執行對應時間事件處理( 回調函數 )**的一個框架; 補充:下文中可能會出現定時任務,它和時間事件基本上是一個東西 那麼現在有一個就有一個問題,該 ...
  • Ubuntu下的串口軟體, 除了 CuteCOM, screen, MiniCOM 以外, 還有一個和 MiniCOM 很像的 PicoCOM. 最近在調試 CH340C 串口的過程中, 發現只有 PicoCOM 的連接Reset才能正常工作, 因此單獨記錄一下. ...
  • 這個問題弄了半天,希望可以幫到你。 首先報一下配置: DELL G15 2023 無線網卡:Intel AX201 BIOS版本為最新(截止到2023.5.15) 首先,打開終端ctrl+alt+T 然後,在終端鍵入: uname -r // 查看內核版本 // ax201 需要內核版本5.2+ / ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...