LINQ to SQL 作為 .NET 框架中操作關聯式資料庫的核心 ORM 技術,其查詢語法將 C# 語言與 SQL 完美融合。本文透過 Northwind 範例資料庫,系統性整理從基本投影、條件篩選、排序分頁,到群組彙總、集合運算(Union/Intersect/Except)、子查詢與 Join 等實戰場景,並對照生成的 T-SQL,助你精準掌握 LINQ 查詢的寫法與效能特徵。
var q = from c in ctx.Customers
select new { 公司名 = c.CompanyName, 地址 = c.Address };
對應 SQL:SELECT [CompanyName], [Address] FROM [Customers]
var q = from emp in ctx.Employees
select new {
姓名 = emp.LastName + emp.FirstName,
雇用年 = emp.HireDate.Value.Year
};
LINQ 會自動轉換為 DATEPART(Year, HireDate)。
var q = from c in ctx.Customers
select new {
ID = c.CustomerID,
聯繫資訊 = new { 職位 = c.ContactTitle, 聯繫人 = c.ContactName }
};
雖然 C# 寫成巢狀,產出的 SQL 仍為扁平化欄位,這點需注意。
var q = from o in ctx.Orders
select new {
訂單號 = o.OrderID,
是否超重 = o.Freight > 100 ? "是" : "否"
};
轉譯為參數化 CASE WHEN Freight > @p0 THEN @p1 ELSE @p2 END,防止 SQL 注入。
var q = from c in ctx.Customers
where c.Country == "France" && c.Orders.Count > 5
select new { 國家 = c.Country, 城市 = c.City, 訂單數 = c.Orders.Count };
子查詢會重複出現於 WHERE 與 SELECT,建議若效能敏感可改用 GroupJoin 或預先載入。
var q = from emp in ctx.Employees
where emp.Employees.Count == 0
orderby emp.HireDate.Value.Year descending, emp.FirstName ascending
select new { 雇用年 = emp.HireDate.Value.Year, 名 = emp.FirstName };
var page2 = (from c in ctx.Customers select c).Skip(10).Take(10);
SQL Server 2005+ 產生 ROW_NUMBER() OVER (ORDER BY ...) 視窗函數,效能優於舊版 TOP 子查詢寫法。
var q = from c in ctx.Customers
group c by c.Country into g
where g.Count() > 5
orderby g.Count() descending
select new { 國家 = g.Key, 顧客數 = g.Count() };
var q = from c in ctx.Customers
group c by new { c.City, c.Country } into g
orderby g.Key.Country, g.Key.City
select new { 國家 = g.Key.Country, 城市 = g.Key.City };
var q = from o in ctx.Orders
group o by new { 條件 = o.Freight > 100 } into g
select new {
數量 = g.Count(),
是否超重 = g.Key.條件 ? "是" : "否"
};
此技巧可將任意邏輯轉為分組鍵,極具彈性。
| 運算子 | SQL 對應 | 去重 |
|---|---|---|
| Union | UNION | 是 |
| Concat | UNION ALL | 否 |
| Intersect | INTERSECT | 是 |
| Except | EXCEPT | 是 |
範例:(from c where c.City.Contains("A") select c).Union(from c where c.ContactName.StartsWith("A") select c).OrderBy(c => c.ContactName)
var q = from c in ctx.Customers
where (from o in ctx.Orders
group o by o.CustomerID into g
where g.Count() > 5 select g.Key).Contains(c.CustomerID)
select c;
轉譯為 WHERE EXISTS (SELECT ... GROUP BY CustomerID HAVING COUNT(*) > 5)。
var cities = new[] { "Brandenburg", "Cowes", "Stavern" };
var q = from c in ctx.Customers where cities.Contains(c.City) select c;
自動參數化為 WHERE City IN (@p0, @p1, @p2)。
var q = from p in ctx.Products
join c in ctx.Categories on p.CategoryID equals c.CategoryID
select p.ProductName;
var q = from p in ctx.Products
join c in ctx.Categories on p.CategoryID equals c.CategoryID into gj
from x in gj.DefaultIfEmpty()
select p.ProductName;
into gj ... DefaultIfEmpty() 是關鍵模式,產生 LEFT OUTER JOIN。
c.Orders.Count)若在迴圈中存取,會產生大量查詢;請善用 DataLoadOptions.LoadWith 預先載入。ExecuteQuery 與 ExecuteCommand 直接呼叫 SP,兼顧效能與維護性。掌握 LINQ to SQL 查詢語法,不僅能以強型別、IntelliSense 輔助撰寫資料存取邏輯,更能透過對照生成的 T-SQL 深入理解 ORM 行為。實務上建議:「簡單查詢用 LINQ,複雜報表用 SP,效能關鍵處用 DataLoadOptions 或原生 SQL」,三者互補,打造健壯且可維護的資料層。