表查詢實戰(zhàn))
1. 項目概述為什么從SqlSugar的基礎查詢開始如果你正在用.NET做后端開發(fā)尤其是涉及到數(shù)據(jù)庫操作那你大概率聽說過或者正在用SqlSugar。它作為一個輕量級的ORM框架這幾年在社區(qū)里的熱度一直不低原因很簡單它足夠簡單也足夠強大能讓你用更少的代碼、更直觀的方式去操作數(shù)據(jù)庫。但很多朋友包括我早期也一樣一上來就想搞“高級功能”比如分庫分表、讀寫分離結果連最基礎的查詢都寫得磕磕絆絆各種報錯效率沒提上去反而踩了一堆坑。所以今天我們不談那些花里胡哨的就扎扎實實地把SqlSugar的基礎查詢給捋清楚。這就像練武功馬步扎不穩(wěn)后面的招式都是花架子。所謂基礎查詢核心就是SELECT語句的那些事怎么查單條、查列表、查數(shù)量、按條件查、排序、分頁。別看這些操作簡單但里面門道不少比如First和Single用錯了會拋異常分頁參數(shù)沒處理好性能直接拉胯還有那個讓人頭疼的ToListAsync和ToList到底該用哪個。我見過不少項目因為基礎查詢寫得隨意導致后期維護困難性能瓶頸也往往出在這些最常用的操作上。因此這篇內容我會結合我這些年踩過的坑和總結的經(jīng)驗把SqlSugar基礎查詢的每一個細節(jié)都掰開揉碎了講目標是讓你看完之后不僅能寫出正確的查詢更能寫出高效、健壯的查詢。無論你是剛接觸SqlSugar的新手還是想鞏固基礎的老手相信都能有所收獲。2. 核心概念與DbContext初始化在深入查詢之前我們必須先打好地基。SqlSugar的核心是圍繞SqlSugarClient或ISqlSugarClient這個對象展開的現(xiàn)在更推薦使用依賴注入的方式通過SqlSugarScope來管理生命周期。理解這個“入口”的配置是避免后續(xù)各種連接問題和性能問題的關鍵。2.1 理解SqlSugarScope與連接配置SqlSugarScope是SqlSugar 5.x之后推薦的單例模式核心對象它內部維護了連接池能自動管理連接的開啟和關閉比直接使用SqlSugarClient更省心。初始化它本質上就是告訴SqlSugar你要連接哪個數(shù)據(jù)庫、用什么賬號、以及一些全局的行為規(guī)則。using SqlSugar; public static class SqlSugarHelper { // 使用SqlSugarScope作為單例 public static readonly SqlSugarScope Db new SqlSugarScope(new ConnectionConfig() { ConnectionString Serverlocalhost;DatabaseTestDB;User Idsa;Passwordyourpassword;, // 你的連接字符串 DbType DbType.SqlServer, // 數(shù)據(jù)庫類型如SqlServer、MySql、PostgreSQL等 IsAutoCloseConnection true, // 自動關閉連接設為true最省心 InitKeyType InitKeyType.Attribute // 實體主鍵、標識列通過特性如[SugarColumn]來識別 }); }這里有幾個參數(shù)必須搞清楚DbType這是你數(shù)據(jù)庫類型的枚舉。填錯了SqlSugar生成的SQL語句就會不對比如把GETDATE()生成到MySQL里。常見的值有DbType.SqlServer、DbType.MySql、DbType.PostgreSQL、DbType.Oracle。IsAutoCloseConnection我強烈建議設為true。這意味著你不需要手動調用Open()和Close()SqlSugar會在需要時從連接池取連接用完后自動歸還。如果你設為false就必須自己管理連接生命周期很容易忘記關閉導致連接泄露。InitKeyType這決定了SqlSugar如何識別實體類的主鍵和自增列。設為Attribute是最清晰的方式通過在實體屬性上打[SugarColumn(IsPrimaryKey true, IsIdentity true)]這樣的標簽來聲明。另一種方式是InitKeyType.SystemTable即從數(shù)據(jù)庫系統(tǒng)表讀取但這種方式有局限性且不如特性聲明靈活直觀。注意在生產(chǎn)環(huán)境中連接字符串絕對不要硬編碼在代碼里。應該從appsettings.json、環(huán)境變量或配置中心讀取。另外對于.NET 6/7/8的項目更推薦在Program.cs中使用依賴注入的方式注冊SqlSugarScope這樣可以更好地與ASP.NET Core的生命周期集成也方便進行單元測試。2.2 實體類映射的“正確姿勢”O(jiān)RM是“對象關系映射”所以實體類是重中之重。一個定義清晰的實體類能讓后續(xù)的查詢、插入、更新操作事半功倍。[SugarTable(Student)] // 指定表名如果類名和表名一致可省略 public class Student { [SugarColumn(IsPrimaryKey true, IsIdentity true)] // 主鍵且自增 public int Id { get; set; } [SugarColumn(Length 50, IsNullable false)] // 字段長度50不可為空 public string Name { get; set; } public int Age { get; set; } [SugarColumn(ColumnName class_id)] // 數(shù)據(jù)庫字段名與屬性名不同時用ColumnName指定 public int ClassId { get; set; } [SugarColumn(IsIgnore true)] // 此屬性不映射到數(shù)據(jù)庫 public string DisplayInfo ${Name}({Age}); [Navigate(NavigateType.OneToOne, nameof(ClassId))] // 一對一導航屬性需要聯(lián)表查詢 public Class Class { get; set; } }關鍵點解析[SugarTable]當你的類名和數(shù)據(jù)庫表名不一致時比如類叫Student表叫t_stu必須用這個特性指明。一致時可省略但我建議顯式寫上代碼更清晰。[SugarColumn]這是最常用的特性。IsPrimaryKey和IsIdentity必須準確標記。這影響到Insert后能否返回自增ID以及Update時能否正確識別WHERE條件。Length對于字符串類型指定數(shù)據(jù)庫字段長度。不指定時SqlSugar會有默認值但為了和數(shù)據(jù)庫設計一致最好明確指定。IsNullable指示字段是否允許為NULL。這會影響Insert和Update時SqlSugar的判空邏輯。ColumnName解決“數(shù)據(jù)庫字段名是下劃線風格C#屬性名是駝峰風格”這類命名差異問題。用了它你就能用student.Name去操作數(shù)據(jù)庫里的name字段。IsIgnore標記后這個屬性會被SqlSugar完全忽略不參與任何數(shù)據(jù)庫操作。常用于計算屬性、臨時字段或敏感信息。導航屬性[Navigate]用于定義對象之間的關系一對一、一對多等。這里有個巨大的坑導航屬性本身不會自動加載數(shù)據(jù)它只是定義了一個關系路徑。你必須使用Includes或Mapper方法進行聯(lián)表查詢時數(shù)據(jù)才會填充到這個屬性里。很多新手以為定義了導航屬性就能直接訪問student.Class.Name結果發(fā)現(xiàn)是null問題就出在這里。3. 基礎查詢操作全解析地基打牢了現(xiàn)在我們開始蓋房子。基礎查詢是使用頻率最高的操作SqlSugar提供了鏈式調用的API寫起來非常流暢。我們從最簡單的開始。3.1 查詢單條記錄First、Single與它們的“坑”當你確定查詢條件最多只返回一條記錄時比如用主鍵查就需要用單條查詢方法。// 假設我們有一個Student表Id是主鍵 var student1 Db.QueryableStudent().Where(s s.Id 1).First(); var student2 Db.QueryableStudent().Where(s s.Id 1).Single(); var student3 Db.QueryableStudent().Where(s s.Id 1).FirstAsync().Result; // 異步版本First()vsSingle()怎么選這是最容易混淆的地方選錯了程序就會拋異常。First()返回序列中的第一個元素。如果序列為空沒查到則拋出InvalidOperationException異常。如果序列有多個元素它只取第一個不會報錯。Single()返回序列中的唯一元素。如果序列為空或多于一個元素都會拋出InvalidOperationException異常。結論與建議當你用唯一性條件查詢時如主鍵Id 1用Single()更安全因為它能幫你驗證“是否真的只有一條”這個假設。如果有人誤刪了數(shù)據(jù)或條件寫錯Single()會立刻用異常告訴你出問題了。當你進行非唯一性條件查詢或者排序后取第一條時必須用First()。例如Db.QueryableOrder().Where(o o.UserId 1001).OrderByDescending(o o.CreateTime).First()獲取用戶最新訂單。異步方法FirstAsync()和SingleAsync()是它們的異步版本在ASP.NET Core等支持異步的上下文中應優(yōu)先使用避免阻塞線程。實操心得我個人的習慣是只要是按主鍵查一律用Single。這相當于加了一道保險。對于其他明確可能返回多條記錄的場景再用First。另外SqlSugar還提供了FirstOrDefault和SingleOrDefault它們在找不到元素時返回null而不是拋異常在業(yè)務邏輯允許“查不到”的情況下非常有用可以避免不必要的異常處理。3.2 查詢列表ToList的同步與異步抉擇查詢多條記錄是最常見的場景對應SQL的SELECT * FROM ...。// 同步查詢直接返回ListStudent ListStudent listSync Db.QueryableStudent().Where(s s.Age 18).ToList(); // 異步查詢返回TaskListStudent推薦在Web應用中使用 ListStudent listAsync await Db.QueryableStudent().Where(s s.Age 18).ToListAsync(); // 帶排序的查詢 ListStudent orderedList Db.QueryableStudent() .Where(s s.ClassId 10) .OrderBy(s s.Age) // 按年齡升序 .OrderByDescending(s s.Name) // 再按姓名降序OrderBy之后調用是追加排序條件 .ToList();核心要點QueryableT()這是查詢的起點它返回一個ISugarQueryableT對象代表一個待執(zhí)行的查詢。Where()指定查詢條件支持豐富的Lambda表達式。SqlSugar會將其轉換為對應的SQLWHERE子句。它是延遲執(zhí)行的只有在調用ToList、First、Count等方法時SQL才會真正發(fā)送到數(shù)據(jù)庫。OrderBy/OrderByDescending排序。可以多次調用相當于SQL中的ORDER BY age ASC, name DESC。ToList()vsToListAsync()這是性能關鍵。ToList()是同步方法。它會阻塞當前線程直到數(shù)據(jù)庫返回所有數(shù)據(jù)。在桌面應用或控制臺程序中可能問題不大。ToListAsync()是異步方法。它不會阻塞當前線程在等待數(shù)據(jù)庫響應時線程可以被釋放去處理其他請求。在ASP.NET Core、Web API等I/O密集型的Web服務器應用中必須優(yōu)先使用異步方法。這能顯著提高服務器的并發(fā)處理能力I/O密集型避免線程池線程被大量阻塞導致服務吞吐量下降。延遲執(zhí)行Deferred Execution的妙用因為Queryable和Where是延遲執(zhí)行的所以你可以在最終執(zhí)行前動態(tài)構建查詢條件。ISugarQueryableStudent query Db.QueryableStudent(); if (!string.IsNullOrEmpty(keyword)) { query query.Where(s s.Name.Contains(keyword)); } if (minAge 0) { query query.Where(s s.Age minAge); } // 直到這里SQL都還沒生成 var result query.OrderBy(s s.Id).ToList(); // 此時才生成SQL并執(zhí)行這種方式比在代碼里拼接SQL字符串要安全、優(yōu)雅得多。3.3 查詢數(shù)量、是否存在與聚合查詢除了查數(shù)據(jù)本身我們經(jīng)常需要知道有多少條數(shù)據(jù)或者做一些統(tǒng)計。// 1. 查詢總記錄數(shù) - Count int totalCount Db.QueryableStudent().Count(); // SELECT COUNT(1) FROM Student int countWithCondition Db.QueryableStudent().Where(s s.Age 20).Count(); // 2. 判斷是否存在符合條件的記錄 - Any bool exists Db.QueryableStudent().Where(s s.Name 張三).Any(); // SELECT 1 FROM Student WHERE name張三 LIMIT 1 // Any比 Count() 0 更高效因為找到一條就返回。 // 3. 聚合查詢 - Max, Min, Sum, Avg int maxAge Db.QueryableStudent().Max(s s.Age); double avgAge Db.QueryableStudent().Avg(s s.Age); decimal totalScore Db.QueryableScore().Where(s s.StudentId 1).Sum(s s.Points); // 4. 分組聚合 - GroupBy var groupResult Db.QueryableStudent() .GroupBy(s s.ClassId) .Select(s new { ClassId s.ClassId, Count SqlFunc.AggregateCount(s.Id), AvgAge SqlFunc.AggregateAvg(s.Age) }) .ToList(); // 生成的SQL類似于SELECT class_id, COUNT(id), AVG(age) FROM Student GROUP BY class_id性能提示用Any()替代Count() 0當你只關心“有沒有”而不關心“有多少”時Any()是更好的選擇。它的SQL通常是SELECT 1 FROM ... WHERE ... LIMIT 1數(shù)據(jù)庫找到第一條匹配記錄就返回效率更高。而Count()需要掃描所有匹配的行。聚合查詢的NULL處理數(shù)據(jù)庫里如果都是NULLMax、Min、Sum、Avg可能會返回NULL。C#中int、double等值類型不能為NULL所以SqlSugar通常會返回默認值如0。如果你的業(yè)務邏輯需要區(qū)分“沒有數(shù)據(jù)”和“總和為0”可以考慮使用可空類型或者先Any()判斷一下。4. 條件構建與分頁查詢實戰(zhàn)實際項目中查詢很少是簡單的SELECT *總是伴隨著動態(tài)的條件和分頁需求。4.1 靈活構建動態(tài)查詢條件前端傳過來的搜索條件五花八門我們需要安全地將其組合到查詢中。錯誤示范SQL注入風險string name Request.Query[name]; // 假設用戶輸入了 OR 11 var sql $SELECT * FROM Student WHERE Name {name}; // 永遠不要這樣寫正確姿勢使用Expression或ConditionalFilter// 方法1使用Expression動態(tài)拼接最常用 public ListStudent SearchStudents(string name, int? minAge, int? maxAge) { var query Db.QueryableStudent(); if (!string.IsNullOrEmpty(name)) { query query.Where(s s.Name.Contains(name)); // Contains會被翻譯為 LIKE %{name}% } if (minAge.HasValue) { query query.Where(s s.Age minAge.Value); } if (maxAge.HasValue) { query query.Where(s s.Age maxAge.Value); } return query.ToList(); } // SqlSugar會將Lambda表達式轉換為參數(shù)化查詢有效防止SQL注入。 // 方法2使用ConditionalFilter適用于更復雜的動態(tài)邏輯 var query Db.QueryableStudent(); query.WhereIF(!string.IsNullOrEmpty(name), s s.Name.Contains(name)) .WhereIF(minAge 0, s s.Age minAge); // WhereIF方法第一個參數(shù)是bool條件為true時才應用后面的過濾條件。代碼更緊湊。關于Contains、StartsWith、EndsWiths.Name.Contains(keyword)生成Name LIKE %keyword%s.Name.StartsWith(keyword)生成Name LIKE keyword%s.Name.EndsWith(keyword)生成Name LIKE %keyword注意前模糊(%keyword)和全模糊(%keyword%)查詢會導致數(shù)據(jù)庫索引失效在大數(shù)據(jù)表上慎用。如果必須用請考慮使用全文檢索技術如Elasticsearch。4.2 分頁查詢的正確實現(xiàn)與性能考量分頁是Web應用的標配。SqlSugar提供了非常便捷的分頁方法但用法不對性能差十倍。// 標準分頁查詢 int pageIndex 1; // 當前頁碼通常從1開始 int pageSize 10; // 每頁條數(shù) RefAsyncint totalCount 0; // 用于接收總記錄數(shù)的引用參數(shù) ListStudent pageList await Db.QueryableStudent() .Where(s s.Age 10) .OrderBy(s s.Id) // 分頁必須排序否則每次返回的順序可能不一致。 .ToPageListAsync(pageIndex, pageSize, totalCount); Console.WriteLine($當前頁數(shù)據(jù){pageList.Count}條總記錄數(shù){totalCount.Value}條); // 通常你需要將 pageList 和 totalCount 一起返回給前端用于生成分頁控件。分頁查詢的三大黃金法則必須排序 (OrderBy)沒有ORDER BY的分頁查詢數(shù)據(jù)庫每次返回的數(shù)據(jù)順序可能都是隨機的取決于執(zhí)行計劃這會導致用戶看到的數(shù)據(jù)錯亂、重復或丟失。這是分頁最最基本的要求。理解ToPageList的參數(shù)ToPageListAsync(pageIndex, pageSize, totalCount)。pageIndex: 頁碼從1開始。pageSize: 每頁大小。totalCount: 一個RefAsyncint類型的輸出參數(shù)。方法執(zhí)行后總記錄數(shù)會填充到這里。這里有個坑這個totalCount是本次查詢條件對應的總記錄數(shù)SqlSugar會額外執(zhí)行一條COUNT(*)的SQL來獲取它。這意味著一次分頁查詢實際上可能產(chǎn)生了兩條SQL一條COUNT(*)一條帶OFFSET ... FETCH ...或LIMIT ...的分頁查詢。警惕深度分頁的性能問題OFFSET分頁即pageIndex很大時在數(shù)據(jù)量大的情況下性能很差。例如OFFSET 10000 ROWS FETCH NEXT 10 ROWS ONLY數(shù)據(jù)庫需要先掃描并跳過前10000行效率低下。優(yōu)化方案對于深度分頁考慮使用“游標分頁”或“鍵集分頁”。即記錄上一頁最后一條記錄的ID或排序字段值下一頁查詢用WHERE id lastId ORDER BY id LIMIT 10。這需要業(yè)務邏輯和前端配合。ToPageList內部發(fā)生了什么以SQL Server為例當你調用ToPageListAsync(2, 10, ref total)時SqlSugar大致會生成并執(zhí)行以下SQL-- 1. 先查總數(shù) SELECT COUNT(1) FROM Student WHERE Age 10; -- 2. 再查分頁數(shù)據(jù) SELECT * FROM ( SELECT ROW_NUMBER() OVER (ORDER BY Id) AS RowIndex, * FROM Student WHERE Age 10 ) AS T WHERE RowIndex BETWEEN 11 AND 20; -- (pageIndex-1)*pageSize1 到 pageIndex*pageSize了解這個你就能明白為什么分頁查詢的成本相對較高。5. 聯(lián)表查詢與導航屬性填充單表查詢滿足不了復雜業(yè)務我們經(jīng)常需要關聯(lián)多張表。SqlSugar提供了兩種主流的聯(lián)表方式Mapper和Includes。5.1 使用Mapper進行手動映射聯(lián)表查詢Mapper方式更接近原生SQL的思維靈活性強適合復雜的多表關聯(lián)場景。// 假設有Student和Class表Student.ClassId 關聯(lián) Class.Id var list Db.QueryableStudent() .LeftJoinClass((s, c) s.ClassId c.Id) // 左連接 .Where((s, c) s.Age 18 c.Name.Contains(實驗班)) .Select((s, c) new StudentViewModel // 自定義返回對象避免循環(huán)引用和性能浪費 { StudentId s.Id, StudentName s.Name, ClassName c.Name, Teacher c.TeacherName }) .ToList(); // 如果需要將關聯(lián)的Class對象填充到Student的導航屬性里可以使用.Mapper var listWithNav Db.QueryableStudent() .LeftJoinClass((s, c) s.ClassId c.Id) .Select((s, c) s) // 主表選擇Student .Mapper(s s.Class, s s.ClassId) // 關鍵將關聯(lián)的Class映射到Student.Class屬性 .ToList(); // 此時listWithNav中每個Student對象的Class屬性都被填充了。Mapper方法詳解MapperT, T2(FuncT, T2 setAction, FuncT, object whereField)這是最常用的重載。setAction: 指定如何設置導航屬性通常是s s.Class。whereField: 指定用于關聯(lián)的字段通常是s s.ClassId。SqlSugar內部會根據(jù)這個關聯(lián)條件將之前Join出來的Class數(shù)據(jù)匹配并設置到對應的Student對象上。優(yōu)點控制力強可以處理非常復雜的多級嵌套關聯(lián)。缺點代碼稍顯繁瑣需要手動指定映射關系。5.2 使用Includes進行自動映射一對一/一對多Includes是Mapper的語法糖在簡單的一對一、一對多關聯(lián)中寫起來更簡潔。// 一對一映射 (Student - Class) var student Db.QueryableStudent() .Includes(s s.Class) // 自動根據(jù)Student.ClassId和Class.Id進行關聯(lián) .First(s s.Id 1); // 現(xiàn)在 student.Class 不為null // 一對多映射 (Class - ListStudent) // 首先在Class實體中定義導航屬性 public class Class { [SugarColumn(IsPrimaryKey true)] public int Id { get; set; } public string Name { get; set; } [Navigate(NavigateType.OneToMany, nameof(Student.ClassId))] // 一對多關聯(lián)字段是Student.ClassId public ListStudent Students { get; set; } } // 查詢時使用Includes var classWithStudents Db.QueryableClass() .Includes(c c.Students) // 自動查詢關聯(lián)的所有Student .First(c c.Id 10); // 現(xiàn)在 classWithStudents.Students 包含了這個班級的所有學生列表Includes的工作原理與局限Includes看起來很美好但它背后可能執(zhí)行了“N1查詢”。以上面的Includes(c c.Students)為例先執(zhí)行一條SQL查詢ClassSELECT * FROM Class WHERE id 10。再執(zhí)行一條SQL查詢關聯(lián)的StudentSELECT * FROM Student WHERE class_id IN (10)。 如果主查詢返回了多個Class比如ToList()那么第二步的SQL就會變成WHERE class_id IN (10, 20, 30...)。這比在一條SQL里用JOIN要清晰但有時效率不如手動優(yōu)化過的JOIN。局限Includes主要適用于一對一、一對多這種明確的導航關系。對于多對多或者關聯(lián)條件非常復雜的情況還是得用Mapper。避坑指南無論是Mapper還是Includes都要警惕循環(huán)引用和數(shù)據(jù)膨脹。循環(huán)引用例如Student有一個Class屬性Class又有一個Students屬性ListStudent。如果你用Includes同時加載了兩邊序列化成JSON時會陷入死循環(huán)。解決方案是使用[JsonIgnore]特性忽略其中一個導航屬性或者使用Select返回DTO數(shù)據(jù)傳輸對象只選擇需要的字段。數(shù)據(jù)膨脹聯(lián)表查詢時如果主表的一條記錄對應子表的多條記錄用JOIN方式會導致主表數(shù)據(jù)重復。Mapper和Includes在內部做了處理會幫你組裝成對象樹避免了這個問題。6. 常見報錯排查與性能優(yōu)化錦囊即使理解了所有概念在實際編碼中依然會遇到各種報錯和性能問題。這里我整理了幾個最常見的問題和解決方法。6.1 高頻報錯排查速查表報錯信息/現(xiàn)象可能原因解決方案SqlSugar.SqlSugarException: Connection open error .1. 連接字符串錯誤。2. 數(shù)據(jù)庫服務未啟動。3. 網(wǎng)絡不通或防火墻攔截。4. 連接池耗盡長時間運行后。1. 仔細檢查連接字符串的服務器、數(shù)據(jù)庫名、用戶名、密碼。2. 確認SQL Server/MySQL等服務已啟動。3. 使用telnet或數(shù)據(jù)庫客戶端測試連通性。4. 檢查代碼中是否有連接未關閉確保IsAutoCloseConnectiontrue或在數(shù)據(jù)庫層面調整連接池設置。SqlSugar.SqlSugarException: The table ‘XXX’ doesn‘t exist.1. 表名寫錯或數(shù)據(jù)庫不存在此表。2. 實體類未用[SugarTable]指定表名且類名與表名不一致。3. 數(shù)據(jù)庫連接到了錯誤的庫。1. 在數(shù)據(jù)庫管理工具中確認表名。2. 在實體類上添加[SugarTable(“實際表名”)]。3. 檢查連接字符串中的Database或Initial Catalog。System.InvalidOperationException: Sequence contains no elements調用First()或Single()時查詢結果為空。使用FirstOrDefault()或SingleOrDefault()替代它們會在無結果時返回null。或者在調用前先用Any()判斷。System.InvalidOperationException: Sequence contains more than one element調用Single()或SingleOrDefault()時查詢結果多于一條。確認你的查詢條件是否足以唯一確定一條記錄如使用主鍵。如果不是改用First()或FirstOrDefault()。導航屬性為null定義了[Navigate]屬性但查詢時沒有使用Includes或Mapper加載。查詢時必須顯式調用.Includes(s s.Class)或.Mapper來加載導航屬性。更新/刪除時提示“找不到主鍵”實體類的主鍵屬性未用[SugarColumn(IsPrimaryKeytrue)]標記。檢查實體類確保主鍵屬性正確標記。自增主鍵通常還需標記IsIdentitytrue。6.2 性能優(yōu)化核心要點只查詢需要的字段Select這是最重要的優(yōu)化原則。避免使用QueryableT().ToList()這種查全表所有字段的方式。// 不好 var list Db.QueryableOrder().ToList(); // SELECT * FROM Order // 好 var list Db.QueryableOrder().Select(o new { o.Id, o.OrderNo, o.CreateTime }).ToList(); // 或者使用DTO var list Db.QueryableOrder().Select(o new OrderBriefDto { Id o.Id, No o.OrderNo }).ToList();網(wǎng)絡傳輸?shù)臄?shù)據(jù)量越少速度越快內存占用也越小。善用索引優(yōu)化Where條件確保查詢條件中的字段尤其是等值查詢和范圍查詢的字段建立了數(shù)據(jù)庫索引。避免在索引列上使用函數(shù)或計算如WHERE YEAR(CreateTime) 2023這會導致索引失效。應改為WHERE CreateTime 2023-01-01 AND CreateTime 2024-01-01。謹慎使用LIKE %keyword%全模糊查詢如前所述它會讓索引失效。異步查詢Async在Web應用中務必使用ToListAsync(),FirstAsync(),CountAsync()等異步方法。這能釋放線程池線程提高應用的并發(fā)處理能力。分頁查詢的優(yōu)化如前所述避免深度分頁。考慮使用“上一頁最后一條ID”的方式進行分頁。如果UI允許可以提供“僅加載更多”的模式而不是傳統(tǒng)的頁碼跳轉。監(jiān)控生成的SQLSqlSugar提供了方便的SQL輸出功能在開發(fā)階段開啟它能幫你發(fā)現(xiàn)潛在的性能問題。Db.Aop.OnLogExecuting (sql, pars) { Console.WriteLine(sql); // 輸出SQL語句 // 也可以輸出參數(shù) Console.WriteLine(string.Join(,, pars?.Select(it it.ParameterName : it.Value))); };觀察生成的SQL是否符合預期有沒有多余的JOIN或者SELECT *。批量操作對于大量數(shù)據(jù)的插入、更新使用SqlSugar提供的InsertRange、UpdateRange等方法或者使用Storageable進行大數(shù)據(jù)處理這比在循環(huán)中執(zhí)行單條SQL語句要高效得多。基礎查詢是SqlSugar的基石掌握這些內容你就能應對日常開發(fā)中80%的數(shù)據(jù)庫查詢場景。記住寫出正確的查詢只是第一步寫出高效的查詢才是我們追求的目標。多觀察生成的SQL多思考業(yè)務場景你的代碼質量會越來越高。在后續(xù)的分享中我們會繼續(xù)深入SqlSugar的增刪改、事務、以及更高級的查詢特性。