LINQ體驗(6)——LINQ to SQL語句之Join和Order By
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 };
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 =
db.Customers
.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推薦資源
作者:李永京(YJingLee's Blog)
出處:http://lyj.cnblogs.com
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。