zoukankan      html  css  js  c++  java
  • 一次快速改寫 SQL Server 高效查詢的範例

    最近線上系統突然出現匯出資料超過 10 筆時,查詢逾時的狀況,在仔細查找之後。

    發現了問題原因,透過應用端與數據端兩邊同時調整,將查詢的效率提昇了約數百倍以上

    首先,原本應用端的商務邏輯為每一分頁筆數固定為10筆,所以使用者最多可以匯出 10 筆資料

    而且原本的商務邏輯是寫成這樣的

                if (condition.LCKeys != null && condition.LCKeys.Count > 0)
                {
                    sql += "AND (LTRIM(RTRIM(STR(T2.[LawNo],20,3)))+LTRIM(RTRIM(STR([LCNo],20,6)))) IN @LCKeys "; //問題點 (IN參數與欄位用了函數做處理)
                    dynamicParams.Add("@LCKeys", condition.LCKeys);
                }
    

      

    這段語法在 SQL Server 執行時,會轉換成以下的範例語法

    (這裡 A 表 與 B表 為 1:N 關聯 )

    SELECT LawNo,LCNo

    FROM A INNER JOIN B

    WHERE 1=1 AND LTRIM(RTRIM(STR(LawNo+LCNo))) IN (‘A1’,’A2’….’A10’)

    在IN條件少的情況下,即使效率不好,還是可以進行資料匯出的

    這次的問題是因為應用商務邏輯修改導致,從原本使用者最多只能匯出 10 筆資料

    改為最多能匯出500筆資料.

    從上述的範例可知 WHERE 條件式的 IN 參數就達 500 個

    此外條件式欄位也因用了函數而走 Index Scan

    在多個參數下,搜索時間自然拉長

    來看看修改前的樣子

    imageimage

    在上圖中測試語句只放了10個 IN 條件參數,可以見到執行效率整體不是挺好的

     

    接下來看看我們如何做應用與數據端的調整

    首先將應用端原本的查詢參數,改為 Table Valued Parameter 形式

    並且將原本的 IN 查找語法,修改為與Table Valued Parameter 做  INNER JOIN

      var dynamicParams = new DynamicParameters();
                var conditionSection = "";
                DataTable dt = new DataTable();
                dt.Columns.Add("LawNo");
                dt.Columns.Add("LCNo");
    
                for (int index = 0; index < condition.LCNos.Count; index++)
                {
                    DataRow row = dt.NewRow();
                    row["LawNo"] = condition.LawNos[index];
                    row["LCNo"] = condition.LCNos[index];
                    dt.Rows.Add(row);
                }
                if (dt.Rows.Count > 0)
                {
                   conditionSection = " INNER JOIN @LawsTempx X ON X.LawNo = T2.LawNo AND X.LCNo = T2.LCNo ";  //加上額外的 INNER JOIN 條件
                   dynamicParams.Add("LawsTempx", dt.AsTableValuedParameter("LawsTemp"));  //將DataTable轉換成TVP傳入數據庫
                }
    

      

    接下來在數據端需要建立一個對應的自訂 Table Type

    image

    最後來看看修改後的結果

    image

    image

    可以看到整個修改完後,Logical read 數下降了快4萬之多,執行計畫也移掉了 Parallelism 執行

    是不是感覺很簡單呢?這個用法不只能用在查找語句的改寫,也可以試著用在大量 Insert 緩慢的情境上 

    各位小夥伴,如果應用端突然出現這種情況的話,可以考慮看看與開發溝通,改用 TVP 進行改寫喲 Smile

    謝謝大家

    補充: 表值參數 (TVP) 的限制
    https://docs.microsoft.com/zh-cn/sql/relational-databases/tables/use-table-valued-parameters-database-engine?view=sql-server-2017#Restrictions

    有下面的限制:

    • SQL Server 不维护表值参数列的统计信息。

    • 表值参数必须作为输入 READONLY 参数传递到 Transact-SQL 例程。 不能在例程体中对表值参数执行诸如 UPDATE、DELETE 或 INSERT 这样的 DML 操作。

    • 不能将表值参数用作 SELECT INTO 或 INSERT EXEC 语句的目标。 表值参数可以在 SELECT INTO 的 FROM 子句中,也可以在 INSERT EXEC 字符串或存储过程中。

     

  • 相关阅读:
    Zabbix 添加内存告警
    Oracle Drop 表数据恢复
    Kubernetes port-forward
    Jenkins指定tag发布到k8s环境
    Jenkins系列之六——拉取指定branch或tag
    MySQL5.7 报错 ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement
    docker部署常见应用
    Vim 中进行文件目录操作
    Oracle使用expdp/impdp迁移数据
    UmengAppDemo【友盟统计SDK集成以及多渠道打包配置,基于V7.5.3版本】
  • 原文地址:https://www.cnblogs.com/KingJaja/p/9048779.html
Copyright © 2011-2022 走看看