1. 程式人生 > >LINQ體驗(6)——LINQ to SQL語句之Join和Order By

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返回值作為欄位值。如圖所示:

GroupJoin示意圖

生成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
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。