史上最全 ——LINQ to SQL語句
轉自:http://www.cnblogs.com/jara/p/3473996.html
史上最全 ——LINQ to SQL語句
LINQ to SQL語句(1)之Where
適用場景:實現過濾,查詢等功能。
說明:與SQL命令中的Where作用相似,都是起到範圍限定也就是過濾作用的,而判斷條件就是它後面所接的子句。Where操作包括3種形式,分別為簡單形式、關係條件形式、First()形式。下面分別用例項舉例下:
1.簡單形式:
例如:使用where篩選在倫敦的客戶
var q = from c in db.Customers where c.City == "London" select c;
再如:篩選1994 年或之後僱用的僱員:
var q = from e in db.Employees where e.HireDate >= new DateTime(1994, 1, 1) select e;
2.關係條件形式:
篩選庫存量在訂貨點水平之下但未斷貨的產品:
var q = from p in db.Products where p.UnitsInStock <= p.ReorderLevel && !p.Discontinued select p;
篩選出UnitPrice 大於10 或已停產的產品:
var q = from p in db.Products where p.UnitPrice > 10m || p.Discontinued select p;
下面這個例子是呼叫兩次where以篩選出UnitPrice大於10且已停產的產品。
var q = db.Products.Where(p=>p.UnitPrice > 10m).Where(p=>p.Discontinued);
3.First()形式:
返回集合中的一個元素,其實質就是在SQL語句中加TOP (1)。
簡單用法:選擇表中的第一個發貨方。
Shipper shipper = db.Shippers.First();
元素:選擇CustomerID 為“BONAP”的單個客戶
Customer cust = db.Customers.First(c => c.CustomerID == "BONAP");
條件:選擇運費大於 10.00 的訂單:
Order ord = db.Orders.First(o => o.Freight > 10.00M);
LINQ to SQL語句(2)之Select/Distinct
適用場景:o(∩_∩)o… 查詢唄。
說明:和SQL命令中的select作用相似但位置不同,查詢表示式中的select及所接子句是放在表示式最後並把子句中的變數也就是結果返回回來;延遲。Select/Distinct操作包括9種形式,分別為簡單用法、匿名型別形式、條件形式、指定型別形式、篩選形式、整形型別形式、巢狀型別形式、本地方法呼叫形式、Distinct形式。
1.簡單用法:
這個示例返回僅含客戶聯絡人姓名的序列。
var q = from c in db.Customers select c.ContactName;
注意:這個語句只是一個宣告或者一個描述,並沒有真正把資料取出來,只有當你需要該資料的時候,它才會執行這個語句,這就是延遲載入(deferred loading)。如果,在宣告的時候就返回的結果集是物件的集合。你可以使用ToList() 或ToArray()方法把查詢結果先進行儲存,然後再對這個集合進行查詢。當然延遲載入(deferred loading)可以像拼接SQL語句那樣拼接查詢語法,再執行它。
2.匿名型別形式:
說明:匿名型別是C#3.0中新特性。其實質是編譯器根據我們自定義自動產生一個匿名的類來幫助我們實現臨時變數的儲存。匿名型別還依賴於另外一個特性:支援根據property來建立物件。比如,var d = new { Name = "s" };編譯器自動產生一個有property叫做Name的匿名類,然後按這個型別分配記憶體,並初始化物件。但是var d = new {"s"};是編譯不通過的。因為,編譯器不知道匿名類中的property的名字。例如string c = "d";var d = new { c}; 則是可以通過編譯的。編譯器會建立一個叫做匿名類帶有叫c的property。 例如下例:new{c,ContactName,c.Phone};ContactName和Phone都是在對映檔案中定義與表中欄位相對應的property。編譯器讀取資料並建立物件時,會建立一個匿名類,這個類有兩個屬性,為ContactName和Phone,然後根據資料初始化物件。另外編譯器還可以重新命名property的名字。
var q = from c in db.Customers select new {c.ContactName, c.Phone};
上面語句描述:使用 SELECT 和匿名型別返回僅含客戶聯絡人姓名和電話號碼的序列
var q = from e in db.Employees select new { Name = e.FirstName + " " + e.LastName, Phone = e.HomePhone };
上面語句描述:使用SELECT和匿名型別返回僅含僱員姓名和電話號碼的序列,並將FirstName和LastName欄位合併為一個欄位“Name”,此外在所得的序列中將HomePhone欄位重新命名為Phone。
var q = from p in db.Products select new { p.ProductID, HalfPrice = p.UnitPrice / 2 };
上面語句描述:使用SELECT和匿名型別返回所有產品的ID以及HalfPrice(設定為產品單價除以2所得的值)的序列。
3.條件形式:
說明:生成SQL語句為:case when condition then else。
var q = from p in db.Products select new { p.ProductName, Availability = p.UnitsInStock - p.UnitsOnOrder < 0 ? "Out Of Stock" : "In Stock" };
上面語句描述:使用SELECT和條件語句返回產品名稱和產品供貨狀態的序列。
4.指定型別形式:
說明:該形式返回你自定義型別的物件集。
var q = from e in db.Employees select new Name { FirstName = e.FirstName, LastName = e.LastName };
上面語句描述:使用SELECT和已知型別返回僱員姓名的序列。
5.篩選形式:
說明:結合where使用,起到過濾作用。
var q = from c in db.Customers where c.City == "London" select c.ContactName;
上面語句描述:使用SELECT和WHERE返回僅含倫敦客戶聯絡人姓名的序列。
6.shaped形式(整形型別):
說明:其select操作使用了匿名物件,而這個匿名物件中,其屬性也是個匿名物件。
var q = from c in db.Customers select new { c.CustomerID, CompanyInfo = new {c.CompanyName, c.City, c.Country}, ContactInfo = new {c.ContactName, c.ContactTitle} };
語句描述:使用SELECT 和匿名型別返回有關客戶的資料的整形子集。查詢顧客的ID和公司資訊(公司名稱,城市,國家)以及聯絡資訊(聯絡人和職位)。
7.巢狀型別形式:
說明:返回的物件集中的每個物件DiscountedProducts屬性中,又包含一個集合。也就是每個物件也是一個集合類。
var q = from o in db.Orders select new { o.OrderID, DiscountedProducts = from od in o.OrderDetails where od.Discount > 0.0 select od, FreeShippingDiscount = o.Freight };
語句描述:使用巢狀查詢返回所有訂單及其OrderID 的序列、打折訂單中專案的子序列以及免送貨所省下的金額。
8.本地方法呼叫形式(LocalMethodCall):
這個例子在查詢中呼叫本地方法PhoneNumberConverter將電話號碼轉換為國際格式。
var q = from c in db.Customers where c.Country == "UK" || c.Country == "USA" select new { c.CustomerID, c.CompanyName, Phone = c.Phone, InternationalPhone = PhoneNumberConverter(c.Country, c.Phone) };
PhoneNumberConverter方法如下:
public string PhoneNumberConverter(string Country, string Phone) { Phone = Phone.Replace(" ", "").Replace(")", ")-"); switch (Country) { case "USA": return "1-" + Phone; case "UK": return "44-" + Phone; default: return Phone; } }
下面也是使用了這個方法將電話號碼轉換為國際格式並建立XDocument
XDocument doc = new XDocument( new XElement("Customers", from c in db.Customers where c.Country == "UK" || c.Country == "USA" select (new XElement("Customer", new XAttribute("CustomerID", c.CustomerID), new XAttribute("CompanyName", c.CompanyName), new XAttribute("InterationalPhone", PhoneNumberConverter(c.Country, c.Phone)) ))));
9.Distinct形式:
說明:篩選欄位中不相同的值。用於查詢不重複的結果集。生成SQL語句為:SELECT DISTINCT [City] FROM [Customers]
var q = ( from c in db.Customers select c.City ) .Distinct();
語句描述:查詢顧客覆蓋的國家。
LINQ to SQL語句(3)之Count/Sum/Min/Max/Avg
適用場景:統計資料吧,比如統計一些資料的個數,求和,最小值,最大值,平均數。
Count
說明:返回集合中的元素個數,返回INT型別;不延遲。生成SQL語句為:SELECT COUNT(*) FROM
1.簡單形式:
得到資料庫中客戶的數量:
var q = db.Customers.Count();
2.帶條件形式:
得到資料庫中未斷貨產品的數量:
var q = db.Products.Count(p => !p.Discontinued);
LongCount
說明:返回集合中的元素個數,返回LONG型別;不延遲。對於元素個數較多的集合可視情況可以選用LongCount來統計元素個數,它返回long型別,比較精確。生成SQL語句為:SELECT COUNT_BIG(*) FROM
var q = db.Customers.LongCount();
Sum
說明:返回集合中數值型別元素之和,集合應為INT型別集合;不延遲。生成SQL語句為:SELECT SUM(…) FROM
1.簡單形式:
得到所有訂單的總運費:
var q = db.Orders.Select(o => o.Freight).Sum();
2.對映形式:
得到所有產品的訂貨總數:
var q = db.Products.Sum(p => p.UnitsOnOrder);
Min
說明:返回集合中元素的最小值;不延遲。生成SQL語句為:SELECT MIN(…) FROM
1.簡單形式:
查詢任意產品的最低單價:
var q = db.Products.Select(p => p.UnitPrice).Min();
2.對映形式:
查詢任意訂單的最低運費:
var q = db.Orders.Min(o => o.Freight);
3.元素:
查詢每個類別中單價最低的產品:
var categories = from p in db.Products group p by p.CategoryID into g select new { CategoryID = g.Key, CheapestProducts = from p2 in g where p2.UnitPrice == g.Min(p3 => p3.UnitPrice) select p2 };
Max
說明:返回集合中元素的最大值;不延遲。生成SQL語句為:SELECT MAX(…) FROM
1.簡單形式:
查詢任意僱員的最近僱用日期:
var q = db.Employees.Select(e => e.HireDate).Max();
2.對映形式:
查詢任意產品的最大庫存量:
var q = db.Products.Max(p => p.UnitsInStock);
3.元素:
查詢每個類別中單價最高的產品:
var categories = from p in db.Products group p by p.CategoryID into g select new { g.Key, MostExpensiveProducts = from p2 in g where p2.UnitPrice == g.Max(p3 => p3.UnitPrice) select p2 };
Average
說明:返回集合中的數值型別元素的平均值。集合應為數字型別集合,其返回值型別為double;不延遲。生成SQL語句為:SELECT AVG(…) FROM
1.簡單形式:
得到所有訂單的平均運費:
var q = db.Orders.Select(o => o.Freight).Average();
2.對映形式:
得到所有產品的平均單價:
var q = db.Products.Average(p => p.UnitPrice);
3.元素:
查詢每個類別中單價高於該類別平均單價的產品:
var categories = from p in db.Products group p by p.CategoryID into g select new { g.Key, ExpensiveProducts = from p2 in g where p2.UnitPrice > g.Average(p3 => p3.UnitPrice) select p2 };
Aggregate
說明:根據輸入的表示式獲取聚合值;不延遲。即是說:用一個種子值與當前元素通過指定的函式來進行對比來遍歷集合中的元素,符合條件的元素保留下來。如果沒有指定種子值的話,種子值預設為集合的第一個元素。
LINQ to SQL語句(4)之Join
適用場景:在我們表關係中有一對一關係,一對多關係,多對多關係等。對各個表之間的關係,就用這些實現對多個表的操作。
說明:在Join操作中,分別為Join(Join查詢), SelectMany(Select一對多選擇)和GroupJoin(分組Join查詢)。 該擴充套件方法對兩個序列中鍵匹配的元素進行inner join操作
SelectMany
說明:我們在寫查詢語句時,如果被翻譯成SelectMany需要滿足2個條件。1:查詢語句中沒有join和into,2:必須出現EntitySet。在我們表關係中有一對一關係,一對多關係,多對多關係等,下面分別介紹一下。
1.一對多關係(1 to Many):
var q = from c in db.Customers from o in c.Orders where c.City == "London" select o;
語句描述:Customers與Orders是一對多關係。即Orders在Customers類中以EntitySet形式出現。所以第二個from是從c.Orders而不是db.Orders裡進行篩選。這個例子在From子句中使用外來鍵導航選擇倫敦客戶的所有訂單。
var q = from p in db.Products where p.Supplier.Country == "USA" && p.UnitsInStock == 0 select p;
語句描述:這一句使用了p.Supplier.Country條件,間接關聯了Supplier表。這個例子在Where子句中使用外來鍵導航篩選其供應商在美國且缺貨的產品。生成SQL語句為:
SELECT [t0].[ProductID], [t0].[ProductName], [t0].[SupplierID], [t0].[CategoryID],[t0].[QuantityPerUnit],[t0].[UnitPrice], [t0].[UnitsInStock], [t0].[UnitsOnOrder],[t0].[ReorderLevel], [t0].[Discontinued] FROM [dbo].[Products] AS [t0] LEFT OUTER JOIN [dbo].[Suppliers] AS [t1] ON [t1].[SupplierID] = [t0].[SupplierID] WHERE ([t1].[Country] = @p0) AND ([t0].[UnitsInStock] = @p1) -- @p0: Input NVarChar (Size = 3; Prec = 0; Scale = 0) [USA] -- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [0]
2.多對多關係(Many to Many):
var q = from e in db.Employees from et in e.EmployeeTerritories where e.City == "Seattle" select new { e.FirstName, e.LastName, et.Territory.TerritoryDescription };
說明:多對多關係一般會涉及三個表(如果有一個表是自關聯的,那有可能只有2個表)。這一句語句涉及Employees, EmployeeTerritories, Territories三個表。它們的關係是1:M:1。Employees和Territories沒有很明確的關係。
語句描述:這個例子在From子句中使用外來鍵導航篩選在西雅圖的僱員,同時列出其所在地區。這條生成SQL語句為:
SELECT [t0].[FirstName], [t0].[LastName], [t2].[TerritoryDescription] FROM [dbo].[Employees] AS [t0] CROSS JOIN [dbo].[EmployeeTerritories] AS [t1] INNER JOIN [dbo].[Territories] AS [t2] ON [t2].[TerritoryID] = [t1].[TerritoryID] WHERE ([t0].[City] = @p0) AND ([t1].[EmployeeID] = [t0].[EmployeeID]) -- @p0: Input NVarChar (Size = 7; Prec = 0; Scale = 0) [Seattle]
3.自聯接關係:
var q = from e1 in db.Employees from e2 in e1.Employees where e1.City == e2.City select new { FirstName1 = e1.FirstName, LastName1 = e1.LastName, FirstName2 = e2.FirstName, LastName2 = e2.LastName, e1.City };
語句描述:這個例子在select 子句中使用外來鍵導航篩選成對的僱員,每對中一個僱員隸屬於另一個僱員,且兩個僱員都來自相同城市。生成SQL語句為:
SELECT [t0].[FirstName] AS [FirstName1], [t0].[LastName] AS [LastName1],[t1].[FirstName] AS [FirstName2], [t1].[LastName] AS [LastName2],[t0].[City] FROM [dbo].[Employees] AS [t0], [dbo].[Employees] AS [t1] WHERE ([t0].[City] = [t1].[City]) AND ([t1].[ReportsTo] = [t0].[EmployeeID])
GroupJoin
像上面所說的,沒有join和into,被翻譯成SelectMany,同時有join和into時,那麼就被翻譯為GroupJoin。在這裡into的概念是對其結果進行重新命名。
1.雙向聯接(Two way join):
此示例顯式聯接兩個表並從這兩個表投影出結果:
var q = from c in db.Customers join o in db.Orders on c.CustomerID equals o.CustomerID into orders select new { c.ContactName, OrderCount = orders.Count() };
說明:在一對多關係中,左邊是1,它每條記錄為c(from c in db.Customers),右邊是Many,其每條記錄叫做o ( join o in db.Orders ),每對應左邊的一個c,就會有一組o,那這一組o,就叫做orders,也就是說,我們把一組o命名為orders,這就是into用途。這也就是為什麼在select語句中,orders可以呼叫聚合函式Count。在T-SQL中,使用其內嵌的T-SQL返回值作為欄位值。如圖所示:
生成SQL語句為:
SELECT [t0].[ContactName], ( SELECT COUNT(*) FROM [dbo].[Orders] AS [t1] WHERE [t0].[CustomerID] = [t1].[CustomerID] ) AS [OrderCount] FROM [dbo].[Customers] AS [t0]
2.三向聯接(There way join):
此示例顯式聯接三個表並分別從每個表投影出結果:
var q = from c in db.Customers join o in db.Orders on c.CustomerID equals o.CustomerID into ords join e in db.Employees on c.City equals e.City into emps select new { c.ContactName, ords = ords.Count(), emps = emps.Count() };
生成SQL語句為:
SELECT [t0].[ContactName], ( SELECT COUNT(*) FROM [dbo].[Orders] AS [t1] WHERE [t0].[CustomerID] = [t1].[CustomerID] ) AS [ords], ( SELECT COUNT(*) FROM [dbo].[Employees] AS [t2] WHERE [t0].[City] = [t2].[City] ) AS [emps] FROM [dbo].[Customers] AS [t0]
3.左外部聯接(Left Outer Join):
此示例說明如何通過使用 此示例說明如何通過使用DefaultIfEmpty() 獲取左外部聯接。在僱員沒有訂單時,DefaultIfEmpty()方法返回null:
var q = from e in db.Employees join o in db.Orders on e equals o.Employee into ords from o in ords.DefaultIfEmpty() select new { e.FirstName, e.LastName, Order = o };
說明:以Employees左表,Orders右表,Orders 表中為空時,用null值填充。Join的結果重新命名ords,使用DefaultIfEmpty()函式對其再次查詢。其最後的結果中有個Order,因為from o in ords.DefaultIfEmpty() 是對ords組再一次遍歷,所以,最後結果中的Order並不是一個集合。但是,如果沒有from o in ords.DefaultIfEmpty() 這句,最後的select語句寫成select new { e.FirstName, e.LastName, Order = ords }的話,那麼Order就是一個集合。
4.投影的Let賦值(Projected let assignment):
說明:let語句是重新命名。let位於第一個from和select語句之間。
這個例子從聯接投影出最終“Let”表示式:
var q = from c in db.Customers join o in db.Orders on c.CustomerID equals o.CustomerID into ords let z = c.City + c.Country from o in ords select new { c.ContactName, o.OrderID, z };
5.組合鍵(Composite Key):
這個例子顯示帶有組合鍵的聯接:
var q = from o in db.Orders from p in db.Products join d in db.OrderDetails on new { o.OrderID, p.ProductID } equals new { d.OrderID, d.ProductID } into details from d in details select new { o.OrderID, p.ProductID, d.UnitPrice };
說明:使用三個表,並且用匿名類來說明:使用三個表,並且用匿名類來表示它們之間的關係。它們之間的關係不能用一個鍵描述清楚,所以用匿名類,來表示組合鍵。還有一種是兩個表之間是用組合鍵表示關係的,不需要使用匿名類。
6.可為null/不可為null的鍵關係(Nullable/Nonnullable Key Relationship):
這個例項顯示如何構造一側可為 null 而另一側不可為 null 的聯接:
var q = from o in db.Orders join e in db.Employees on o.EmployeeID equals (int?)e.EmployeeID into emps from e in emps select new { o.OrderID, e.FirstName };
LINQ to SQL語句(5)之Order By
適用場景:對查詢出的語句進行排序,比如按時間排序等等。
說明:按指定表示式對集合排序;延遲,:按指定表示式對集合排序;延遲,預設是升序,加上descending表示降序,對應的擴充套件方法是OrderBy和OrderByDescending
1.簡單形式
這個例子使用 orderby 按僱用日期對僱員進行排序:
var q = from e in db.Employees orderby e.HireDate select e;
說明:預設為升序
2.帶條件形式
注意:Where和Order By的順序並不重要。而在T-SQL中,Where和Order By有嚴格的位置限制。
var q = from o in db.Orders where o.ShipCity == "London" orderby o.Freight select o;
語句描述:使用where和orderby按運費進行排序。
3.降序排序
var q = from p in db.Products orderby p.UnitPrice descending select p;
4.ThenBy
語句描述:使用複合的 orderby 對客戶進行排序,進行排序:
var q = from c in db.Customers orderby c.City, c.ContactName select c;
說明:按多個表示式進行排序,例如先按City排序,當City相同時,按ContactName排序。這一句用Lambda表示式像這樣寫:
var q = .OrderBy(c => c.City) .ThenBy(c => c.ContactName).ToList();
在T-SQL中沒有ThenBy語句,其依然翻譯為OrderBy,所以也可以用下面語句來表達:
var q = db.Customers .OrderBy(c => c.ContactName) .OrderBy(c => c.City).ToList();
所要注意的是,多個OrderBy操作時,級連方式是按逆序。對於降序的,用相應的降序操作符替換即可。
var q = db.Customers .OrderByDescending(c => c.City) .ThenByDescending(c => c.ContactName).ToList();
需要說明的是,OrderBy操作,不支援按type排序,也不支援匿名類。比如
var q = db.Customers .OrderBy(c => new { c.City, c.ContactName }).ToList();
會被丟擲異常。錯誤是前面的操作有匿名類,再跟OrderBy時,比較的是類別。比如
var q = db.Customers .Select(c => new { c.City, c.Address }) .OrderBy(c => c).ToList();
如果你想使用OrderBy(c => c),其前提條件是,前面步驟中,所產生的物件的類別必須為C#語言的基本型別。比如下句,這裡City為string型別。
var q = db.Customers .Select(c => c.City) .OrderBy(c => c).ToList();
5.ThenByDescending
這兩個擴充套件方式都是用在OrderBy/OrderByDescending之後的,第一個ThenBy/ThenByDescending擴充套件方法作為第二位排序依據,第二個ThenBy/ThenByDescending則作為第三位排序依據,以此類推
var q = from o in db.Orders where o.EmployeeID == 1 orderby o.ShipCountry, o.Freight descending select o;
語句描述:使用orderby先按發往國家再按運費從高到低的順序對 EmployeeID 1 的訂單進行排序。
6.帶GroupBy形式
var q = from p in db.Products group p by p.CategoryID into g orderby g.Key select new { g.Key, MostExpensiveProducts = from p2 in g where p2.UnitPrice == g.Max(p3 => p3.UnitPrice) select p2 };
語句描述:使用orderby、Max 和 Group By 得出每種類別中單價最高的產品,並按 CategoryID 對這組產品進行排序。
LINQ to SQL語句(6)之Group By/Having
適用場景:分組資料,為我們查詢資料縮小範圍。
說明:分配並返回對傳入引數進行分組操作後的可列舉物件。分組;延遲
1.簡單形式:
var q = from p in db.Products group p by p.CategoryID into g select g;
語句描述:使用Group By按CategoryID劃分產品。
說明:from p in db.Products 表示從表中將產品物件取出來。group p by p.CategoryID into g表示對p按CategoryID欄位歸類。其結果命名為g,一旦重新命名,p的作用域就結束了,所以,最後select時,只能select g。當然,也不必重新命名可以這樣寫:
var q = from p in db.Products group p by p.CategoryID;
我們用示意圖表示:
如果想遍歷某類別中所有記錄,這樣:
foreach (var gp in q) { if (gp.Key == 2) { foreach (var item in gp) { //do something } } }
2.Select匿名類:
var q = from p in db.Products group p by p.CategoryID into g select new { CategoryID = g.Key, g };
說明:在這句LINQ語句中,有2個property:CategoryID和g。這個匿名類,其實質是對返回結果集重新進行了包裝。把g的property封裝成一個完整的分組。如下圖所示:
如果想遍歷某匿名類中所有記錄,要這麼做:
foreach (var gp in q) { if (gp.CategoryID == 2) { foreach (var item in gp.g) { //do something } } }
3.最大值
var q = from p in db.Products group p by p.CategoryID into g select new { g.Key, MaxPrice = g.Max(p => p.UnitPrice) };
語句描述:使用Group By和Max查詢每個CategoryID的最高單價。
說明:先按CategoryID歸類,判斷各個分類產品中單價最大的Products。取出CategoryID值,並把UnitPrice值賦給MaxPrice。
4.最小值
var q = from p in db.Products group p by p.CategoryID into g select new { g.Key, MinPrice = g.Min(p => p.UnitPrice) };
語句描述:使用Group By和Min查詢每個CategoryID的最低單價。
說明:先按CategoryID歸類,判斷各個分類產品中單價最小的Products。取出CategoryID值,並把UnitPrice值賦給MinPrice。
5.平均值
var q = from p in db.Products group p by p.CategoryID into g select new { g.Key, AveragePrice = g.Average(p => p.UnitPrice) };
語句描述:使用Group By和Average得到每個CategoryID的平均單價。
說明:先按CategoryID歸類,取出CategoryID值和各個分類產品中單價的平均值。
6.求和
var q = from p in db.Products group p by p.CategoryID into g select new { g.Key, TotalPrice = g.Sum(p => p.UnitPrice) };
語句描述:使用Group By和Sum得到每個CategoryID 的單價總計。
說明:先按CategoryID歸類,取出CategoryID值和各個分類產品中單價的總和。
7.計數
var q = from p in db.Products group p by p.CategoryID into g select new { g.Key, NumProducts = g.Count() };
語句描述:使用Group By和Count得到每個CategoryID中產品的數量。
說明:先按CategoryID歸類,取出CategoryID值和各個分類產品的數量。
8.帶條件計數
var q = from p in db.Products group p by p.CategoryID into g select new { g.Key, NumProducts = g.Count(p => p.Discontinued) };
語句描述:使用Group By和Count得到每個CategoryID中斷貨產品的數量。
說明:先按CategoryID歸類,取出CategoryID值和各個分類產品的斷貨數量。 Count函式裡,使用了Lambda表示式,Lambda表示式中的p,代表這個組裡的一個元素或物件,即某一個產品。
9.Where限制
var q = from p in db.Products group p by p.CategoryID into g where g.Count() >= 10 select new { g.Key, ProductCount = g.Count() };
語句描述:根據產品的―ID分組,查詢產品數量大於10的ID和產品數量。這個示例在Group By子句後使用Where子句查詢所有至少有10種產品的類別。
說明:在翻譯成SQL語句時,在最外層嵌套了Where條件。
10.多列(Multiple Columns)
var categories = from p in db.Products group p by new { p.CategoryID, p.SupplierID } into g select new { g.Key, g };
語句描述:使用Group By按CategoryID和SupplierID將產品分組。
說明:既按產品的分類,又按供應商分類。在by後面,new出來一個匿名類。這裡,Key其實質是一個類的物件,Key包含兩個Property:CategoryID、SupplierID。用g.Key.CategoryID可以遍歷CategoryID的值。
11.表示式(Expression)
var categories = from p in db.Products group p by new { Criterion = p.UnitPrice > 10 } into g select g;
語句描述:使用Group By返回兩個產品序列。第一個序列包含單價大於10的產品。第二個序列包含單價小於或等於10的產品。
說明:按產品單價是否大於10分類。其結果分為兩類,大於的是一類,小於及等於為另一類。
LINQ to SQL語句(7)之Exists/In/Any/All/Contains
適用場景:用於判斷集合中元素,進一步縮小範圍。
Any
說明:用於判斷集合中是否有元素滿足某一條件;不延遲。(若條件為空,則集合只要不為空就返回True,否則為False)。有2種形式,分別為簡單形式和帶條件形式。
1.簡單形式:
僅返回沒有訂單的客戶:
var q = from c in db.Customers where !c.Orders.Any() select c;
生成SQL語句為:
SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], [t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax] FROM [dbo].[Customers] AS [t0] WHERE NOT (EXISTS( SELECT NULL AS [EMPTY] FROM [dbo].[Orders] AS [t1] WHERE [t1].[CustomerID] = [t0].[CustomerID] ))
2.帶條件形式:
僅返回至少有一種產品斷貨的類別:
var q = from c in db.Categories where c.Products.Any(p => p.Discontinued) select c;
生成SQL語句為:
SELECT [t0].[CategoryID], [t0].[CategoryName], [t0].[Description], [t0].[Picture] FROM [dbo].[Categories] AS [t0] WHERE EXISTS( SELECT NULL AS [EMPTY] FROM [dbo].[Products] AS [t1] WHERE ([t1].[Discontinued] = 1) AND ([t1].[CategoryID] = [t0].[CategoryID]) )
All
說明:用於判斷集合中所有元素是否都滿足某一條件;不延遲
1.帶條件形式
var q = from c in db.Customers where c.Orders.All(o => o.ShipCity == c.City) select c;
語句描述:這個例子返回所有訂單都運往其所在城市的客戶或未下訂單的客戶。
Contains
說明:用於判斷集合中是否包含有某一元素;不延遲。它是對兩個序列進行連線操作的。
string[] customerID_Set = new string[] { "AROUT", "BOLID", "FISSA" }; var q = ( from o in db.Orders where customerID_Set.Contains(o.CustomerID) select o).ToList();
語句描述:查詢"AROUT", "BOLID" 和 "FISSA" 這三個客戶的訂單。先定義了一個數組,在LINQ to SQL中使用Contains,陣列中包含了所有的CustomerID,即返回結果中,所有的CustomerID都在這個集合內。也就是in。 你也可以把陣列的定義放在LINQ to SQL語句裡。比如:
var q = ( from o in db.Orders where ( new string[] { "AROUT", "BOLID", "FISSA" }) .Contains(o.CustomerID) select o).ToList();
Not Contains則取反:
var q = ( from o in db.Orders where !( new string[] { "AROUT", "BOLID", "FISSA" }) .Contains(o.CustomerID) select o).ToList();
1.包含一個物件:
var order = (from o in db.Orders where o.OrderID == 10248 select o).First(); var q = db.Customers.Where(p => p.Orders.Contains(order)).ToList(); foreach (var cust in q) { foreach (var ord in cust.Orders) { //do something } }
語句描述:這個例子使用Contain查詢哪個客戶包含OrderID為10248的訂單。
2.包含多個值:
string[] cities = new string[] { "Seattle", "London", "Vancouver", "Paris" }; var q = db.Customers.Where(p=>cities.Contains(p.City)).ToList();
語句描述:這個例子使用Contains查詢其所在城市為西雅圖、倫敦、巴黎或溫哥華的客戶。
LINQ to SQL語句(8)之Concat/Union/Intersect/Except
適用場景:對兩個集合的處理,例如追加、合併、取相同項、相交項等等。
Concat(連線)
說明:連線不同的集合,不會自動過濾相同項;延遲。
1.簡單形式:
var q = ( from c in db.Customers select c.Phone ).Concat( from c in db.Customers select c.Fax ).Concat( from e in db.Employees select e.HomePhone );
語句描述:返回所有消費者和僱員的電話和傳真。
2.複合形式:
var q = ( from c in db.Customers select new { Name = c.CompanyName, c.Phone } ).Concat( from e in db.Employees select new { Name = e.FirstName + " " + e.LastName, Phone = e.HomePhone } );
語句描述:返回所有消費者和僱員的姓名和電話。
Union(合併)
說明:連線不同的集合,自動過濾相同項;延遲。即是將兩個集合進行合併操作,過濾相同的項。
var q = ( from c in db.Customers select c.Country ).Union( from e in db.Employees select e.Country );
語句描述:查詢顧客和職員所在的國家。
Intersect(相交)
說明:取相交項;延遲。即是獲取不同集合的相同項(交集)。即先遍歷第一個集合,找出所有唯一的元素,然後遍歷第二個集合,並將每個元素與前面找出的元素作對比,返回所有在兩個集合內都出現的元素。
var q = ( from c in db.Customers select c.Country ).Intersect( from e in db.Employees select e.Country );
語句描述:查詢顧客和職員同在的國家。
Except(與非)
說明:排除相交項;延遲。即是從某集合中刪除與另一個集合中相同的項。先遍歷第一個集合,找出所有唯一的元素,然後再遍歷第二個集合,返回第二個集合中所有未出現在前面所得元素集合中的元素。
var q = ( from c in db.Customers select c.Country ).Except( from e in db.Employees select e.Country );
語句描述:查詢顧客和職員不同的國家。
LINQ to SQL語句(9)之Top/Bottom和Paging和SqlMethods
適用場景:適量的取出自己想要的資料,不是全部取出,這樣效能有所加強。
Take
說明:獲取集合的前n個元素;延遲。即只返回限定數量的結果集。
var q = ( from e in db.Employees orderby e.HireDate select e) .Take(5);
語句描述:選擇所僱用的前5個僱員。
Skip
說明:跳過集合的前n個元素;延遲。即我們跳過給定的數目返回後面的結果集。
var q = ( from p in db.Products orderby p.UnitPrice descending select p) .Skip(10);
語句描述:選擇10種最貴產品之外的所有產品。
TakeWhile
說明:直到某一條件成立就停止獲取;延遲。即用其條件去依次判斷源序列中的元素,返回符合判斷條件的元素,該判斷操作將在返回false或源序列的末尾結束 。
SkipWhile
說明:直到某一條件成立就停止跳過;延遲。即用其條件去判斷源序列中的元素並且跳過第一個符合判斷條件的元素,一旦判斷返回false,接下來將不再進行判斷並返回剩下的所有元素。
Paging(分頁)操作
適用場景:結合Skip和Take就可實現對資料分頁操作。
1.索引
var q = ( from c in db.Customers orderby c.ContactName select c) .Skip(50) .Take(10);
語句描述:使用Skip和Take運算子進行分頁,跳過前50條記錄,然後返回接下來10條記錄,因此提供顯示Products表第6頁的資料。
2.按唯一鍵排序
var q = ( from p in db.Products where p.ProductID > 50 orderby p.ProductID select p) .Take(10);
語句描述:使用Where子句和Take運算子進行分頁,首先篩選得到僅50 (第5頁最後一個ProductID)以上的ProductID,然後按ProductID排序,最後取前10個結果,因此提供Products表第6頁的資料。請注意,此方法僅適用於按唯一鍵排序的情況。
SqlMethods操作
在LINQ to SQL語句中,為我們提供了SqlMethods操作,進一步為我們提供了方便,例如Like方法用於自定義通配表示式,Equals用於相比較是否相等。
Like
自定義的通配表示式。%表示零長度或任意長度的字串;_表示一個字元;[]表示在某範圍區間的一個字元;[^]表示不在某範圍區間的一個字元。比如查詢消費者ID以“C”開頭的消費者。
var q = from c in db.Customers where SqlMethods.Like(c.CustomerID, "C%") select c;
比如查詢消費者ID沒有“AXOXT”形式的消費者:
var q = from c in db.Customers where !SqlMethods.Like(c.CustomerID, "A_O_T") select c;
DateDiffDay
說明:在兩個變數之間比較。分別有:DateDiffDay、DateDiffHour、DateDiffMillisecond、DateDiffMinute、DateDiffMonth、DateDiffSecond、DateDiffYear
var q = from o in db.Orders where SqlMethods .DateDiffDay(o.OrderDate, o.ShippedDate) < 10 select o;
語句描述:查詢在建立訂單後的 10 天內已發貨的所有訂單。
已編譯查詢操作(Compiled Query)
說明:在之前我們沒有好的方法對寫出的SQL語句進行編輯重新查詢,現在我們可以這樣做,看下面一個例子:
//1.建立compiled query NorthwindDataContext db = new NorthwindDataContext(); var fn = CompiledQuery.Compile( (NorthwindDataContext db2, string city) => from c in db2.Customers where c.City == city select c); //2.查詢城市為London的消費者,用LonCusts集合表示,這時可以用資料控制元件繫結 var LonCusts = fn(db, "London"); //3.查詢城市為Seattle的消費者 var SeaCusts = fn(db, "Seattle");
語句描述:這個例子建立一個已編譯查詢,然後使用它檢索輸入城市的客戶。
LINQ to SQL語句(10)之Insert
1.簡單形式
說明:new一個物件,使用InsertOnSubmit方法將其加入到對應的集合中,使用SubmitChanges()提交到資料庫。
NorthwindDataContext db = new NorthwindDataContext(); var newCustomer = new Customer { CustomerID = "MCSFT", CompanyName = "Microsoft", ContactName = "John Doe", ContactTitle = "Sales Manager", Address = "1 Microsoft Way", City = "Redmond", Region = "WA", PostalCode = "98052", Country = "USA", Phone = "(425) 555-1234", Fax = null }; db.Customers.InsertOnSubmit(newCustomer); db.SubmitChanges();
語句描述:使用InsertOnSubmit方法將新客戶新增到Customers 表物件。呼叫SubmitChanges 將此新Customer儲存到資料庫。
2.一對多關係
說明:Category與Product是一對多的關係,提交Category(一端)的資料時,LINQ to SQL會自動將Product(多端)的資料一起提交。
var newCategory = new Category { CategoryName = "Widgets", Description = "Widgets are the ……" }; var newProduct = new Product { ProductName = "Blue Widget", UnitPrice = 34.56M, Category = newCategory }; db.Categories.InsertOnSubmit(newCategory); db.SubmitChanges();
語句描述:使用InsertOnSubmit方法將新類別新增到Categories表中,並將新Product物件新增到與此新Category有外來鍵關係的Products表中。呼叫SubmitChanges將這些新物件及其關係儲存到資料庫。
3.多對多關係
說明:在多對多關係中,我們需要依次提交。
var newEmployee = new Employee { FirstName = "Kira", LastName = "Smith" }; var newTerritory = new Territory { TerritoryID = "12345", TerritoryDescription = "Anytown", Region = db.Regions.First() }; var newEmployeeTerritory = new EmployeeTerritory { Employee = newEmployee, Territory = newTerritory }; db.Employees.InsertOnSubmit(newEmployee); db.Territories.InsertOnSubmit(newTerritory); db.EmployeeTerritories.InsertOnSubmit(newEmployeeTerritory); db.SubmitChanges();
語句描述:使用InsertOnSubmit方法將新僱員新增到Employees 表中,將新Territory新增到Territories表中,並將新EmployeeTerritory物件新增到與此新Employee物件和新Territory物件有外來鍵關係的EmployeeTerritories表中。呼叫SubmitChanges將這些新物件及其關係保持到資料庫。
4.使用動態CUD重寫(Override using Dynamic CUD)
說明:CUD就是Create、Update、Delete的縮寫。下面的例子就是新建一個ID(主鍵)為32的Region,不考慮資料庫中有沒有ID為32的資料,如果有則替換原來的資料,沒有則插入。
Region nwRegion = new Region() { RegionID = 32, RegionDescription = "Rainy" }; db.Regions.InsertOnSubmit(nwRegion); db.SubmitChanges();
語句描述:使用DataContext提供的分部方法InsertRegion插入一個區域。對SubmitChanges 的呼叫呼叫InsertRegion 重寫,後者使用動態CUD執行Linq To SQL生成的預設SQL查詢。
LINQ to SQL語句(11)之Update
說明:更新操作,先獲取物件,進行修改操作之後,直接呼叫SubmitChanges()方法即可提交。注意,這裡是在同一個DataContext中,對於不同的DataContex看下面的講解。
1.簡單形式
Customer cust = db.Customers.First(c => c.CustomerID == "ALFKI"); cust.ContactTitle = "Vice President"; db.SubmitChanges();
語句描述:使用SubmitChanges將對檢索到的一個Customer物件做出的更新保持回資料庫。
2.多項更改
var q = from p in db.Products where p.CategoryID == 1 select p; foreach (var p in q) { p.UnitPrice += 1.00M; } db.SubmitChanges();
語句描述:使用SubmitChanges將對檢索到的進行的更新保持回資料庫。
LINQ to SQL語句(12)之Delete和使用Attach
1.簡單形式
說明:呼叫DeleteOnSubmit方法即可。
OrderDetail orderDetail = db.OrderDetails.First (c => c.OrderID == 10255 && c.ProductID == 36); db.OrderDetails.DeleteOnSubmit(orderDetail); db.SubmitChanges();
語句描述:使用DeleteOnSubmit方法從OrderDetail 表中刪除OrderDetail物件。呼叫SubmitChanges 將此刪除保持到資料庫。
2.一對多關係
說明:Order與OrderDetail是一對多關係,首先DeleteOnSubmit其OrderDetail(多端),其次DeleteOnSubmit其Order(一端)。因為一端是主鍵。
var orderDetails = from o in db.OrderDetails where o.Order.CustomerID == "WARTH" && o.Order.EmployeeID == 3 select o; var order = (from o in db.Orders where o.CustomerID == "WARTH" && o.EmployeeID == 3 select o).First(); foreach (OrderDetail od in orderDetails) { db.OrderDetails.DeleteOnSubmit(od); } db.Orders.DeleteOnSubmit(order); db.SubmitChanges();
語句描述語句描述:使用DeleteOnSubmit方法從Order 和Order Details表中刪除Order和Order Detail物件。首先從Order Details刪除,然後從Orders刪除。呼叫SubmitChanges將此刪除保持到資料庫。
3.推理刪除(Inferred Delete)
說明:Order與OrderDetail是一對多關係,在上面的例子,我們全部刪除CustomerID為WARTH和EmployeeID為3 的資料,那麼我們不須全部刪除呢?例如Order的OrderID為10248的OrderDetail有很多,但是我們只要刪除ProductID為11的OrderDetail。這時就用Remove方法。
Order order = db.Orders.First(x => x.OrderID == 10248); OrderDetail od = order.OrderDetails.First(d => d.ProductID == 11); order.OrderDetails.Remove(od); db.SubmitChanges();
語句描述語句描述:這個例子說明在實體物件的引用實體將該物件從其EntitySet 中移除時,推理刪除如何導致在該物件上發生實際的刪除操作。僅當實體的關聯對映將DeleteOnNull設定為true且CanBeNull 為false 時,才會發生推理刪除行為。
使用Attach更新(Update with Attach)
說明:在對於在不同的DataContext之間,使用Attach方法來更新資料。例如在一個名為tempdb的NorthwindDataContext中,查詢出Customer和Order,在另一個NorthwindDataContext中,Customer的地址更新為123 First Ave,Order的CustomerID 更新為CHOPS。
//通常,通過從其他層反序列化 XML 來獲取要附加的實體 //不支援將