关于Linq翻译Inner join ,Left join (本文为转载)
我們先來一段最基本的 LINQ to SQL 使用類似 T-SQL 的 INNER JOIN 資料查詢語法:
from c in Categories
from o in c.Products
select new { c.CategoryName, o.ProductID, o.ProductName }
我們可以從上述 LINQ 語法上就知道有兩個資料來源(因為有兩個 from 子句),而第二個 from 使用的是第一個 from 資料來源的關連屬性,因此產生 JOIN 需求,這段 LINQ 語法所產生的 T-SQL 語句如下:
SELECT [t0].[CategoryName], [t1].[ProductID], [t1].[ProductName]
FROM [Categories] AS [t0], [Products] AS [t1]
WHERE [t1].[CategoryID] = [t0].[CategoryID]
接著我們來看看另一個 INNER JOIN 的資料查詢語法,我們使用語意更為明確的 join 子句:
from c in Categories
join o in Products on c.CategoryID equals o.CategoryID
select new { c.CategoryName, o.ProductID, o.ProductName }
我們可以從上述 LINQ 語法上知道有一個 Categories 資料來源,並且 join 另一個 Products 資料來源,當中用這個兩個實體的其中一個 CategoryID 屬性進行 join 動作,這是一個非常明確的 join 語法,其語法與 T-SQL 的 join 語法非常相近,這段 LINQ 語法所產生的 T-SQL 語句如下:
SELECT [t0].[CategoryName], [t1].[ProductID], [t1].[ProductName]
FROM [Categories] AS [t0]
INNER JOIN [Products] AS [t1] ON ([t0].[CategoryID]) = [t1].[CategoryID]
這兩段 LINQ 語法所執行的結果資料一模一樣,不過產生的 T-SQL 不一樣,所以透過 LINQ to SQL 會自動將設定的 LINQ 語法產生出合適的 T-SQL 語法。不過,我們都知道資料庫查詢的 SQL 語法有很多調校的技巧,透過 LINQ to SQL 之後自然會變的不容易調校,所以針對某些 LINQ 語法還是有可能產生效能不彰的狀況需要自行處理。
舉個例子來說,以下 LINQ 語法除了 join 外還在 select 的地方有隱含的使用了另一個 JOIN 條件:
from c in Categories
join o in Products on c.CategoryID equals o.CategoryID
select new { o.Category.CategoryName ,o.ProductID, o.ProductName }
因此會產生出一個詭異且無效率的 T-SQL 語法如下,同時有 INNER JOIN 與 LEFT OUTER JOIN 的狀況:
SELECT [t2].[CategoryName], [t0].[ProductID], [t0].[ProductName]
FROM [Products] AS [t0]
INNER JOIN [Categories] AS [t1] ON [t0].[CategoryID] = ([t1].[CategoryID])
LEFT OUTER JOIN [Categories] AS [t2] ON [t2].[CategoryID] = [t0].[CategoryID]
這時我們修正 LINQ 語法如下:
from c in Categories
join o in Products on c.CategoryID equals o.CategoryID
select new { c.CategoryName, o.ProductID, o.ProductName }
這時就會產生預期的 T-SQL 語法:
SELECT [t0].[CategoryName], [t1].[ProductID], [t1].[ProductName]
FROM [Categories] AS [t0]
INNER JOIN [Products] AS [t1] ON ([t0].[CategoryID]) = [t1].[CategoryID]
雖然 LINQ 出現的目的就是希望你改用「物件」與「實體」的角度來思考資料的查詢方式,不過如果你能瞭解從 LINQ 轉換到 T-SQL 的過程將有助於你寫出更有效率的 LINQ 查詢語法。
瞭解 INNER JOIN 之後,我們再來看一個 T-SQL 中常見的 LEFT JOIN 語法的 LINQ 要如何撰寫:
from o in Products
select new { o.Category.CategoryName, o.ProductID, o.ProductName }
這段 LINQ 語法我們只取出一個 Products 資料來源,但是在 select 子句卻額外取得 Category 關連實體的資料,這個關連是「多對一」的關連,所以一個 Product 實體只會取得一個 Category 實體,所以轉換 T-SQL 之後你會發現 LEFT JOIN 的左邊就是 Products,而右邊就變成 Categories,其所產生的 T-SQL 語句如下:
SELECT [t1].[CategoryName], [t0].[ProductID], [t0].[ProductName]
FROM [Products] AS [t0]
LEFT OUTER JOIN [Categories] AS [t1] ON [t1].[CategoryID] = [t0].[CategoryID]
雖然你看到的是一個 LEFT JOIN 語法,不過這並不是我們常見的那種 JOIN 狀況,比較複雜的狀況是當我們左側的實體為 Categories 以及右側的實體為 Products 的狀況,這是「一對多」的關連,複雜度相對提高一些。
由於 INNER JOIN 是 交集(AND) 的概念,當兩個 JOIN 的實體都有資料時才有資料。而 LEFT JOIN 的概念卻是「左邊的資料全部都要有,即便右邊沒有資料也要以 null 代替」,因此我們的 LINQ to SQL 語法如下就要特別注意資料來源出現的順序性:
我們先重寫文章稍早出現的過的 INNER JOIN 語法如下:
from c in Categories
join o in Products on c.CategoryID equals o.CategoryID
select new { c.CategoryName, o.ProductName }
如果要改成 LEFT JOIN 語法,則必須修改成以下 LINQ 語法:
from c in Categories
join o in Products on c.CategoryID equals o.CategoryID into ps
from o in ps.DefaultIfEmpty()
select new { c.CategoryName, o.ProductName }
因為撰在 LINQ 撰寫 LEFT JOIN 時的觀念與 T-SQL 不太一樣,要用「物件」的角度比較能理解其語法所代表的意義,所以上述的語法可以這樣解釋:
取出 Categories 資料並放入 c 變數,接著再與 Products 做 JOIN 並將 JOIN 後的結果放入 ps 變數(注意:並非第二行的 o 變數),這時再宣告一個資料來源 o 其資料來自於「修改過的 ps 變數」,且修改的方式為 ps.DefaultIfEmpty(),意即代表若 ps ( 也就是 Products 經過 join 篩選後的資料 ) 無任何資料時,還是回傳一筆 Default 的資料(也就是預設的 null 資料)。
這一段 LINQ 所產生的 T-SQL 如下:
SELECT [t0].[CategoryName], [t1].[ProductName] AS [ProductName]
FROM [Categories] AS [t0]
LEFT OUTER JOIN [Products] AS [t1] ON ([t0].[CategoryID]) = [t1].[CategoryID]
因此當你需要撰寫「多對一」關連情況下正確的使用 LINQ 查詢語法做 LEFT JOIN 就必須這樣來撰寫!
不過有一點必須注意,那就是當 o 取得的物件為 null 時,雖然 select 子句中的 o.ProductName 不會引發例外,而且會回傳 null,但是若你在 select 子句中設定 o.ProductID 的話,那就會引發例外,因為 null 並無法正確轉型成 Int32 型別,錯誤訊息是:InvalidOperationException - 無法將 Null 值指派給型別 System.Int32 的成員,該型別不可為 Null 值。
文章来源:http://blog.miniasp.com/post/2010/10/14/LINQ-to-SQL-Query-Tips-INNER-JOIN-and-LEFT-JOIN.aspx
关于Linq翻译Inner join ,Left join (本文为转载)的更多相关文章
- Linq中join & group join & left join 的用法
Linq中join & group join & left join 的用法 2013-01-30 11:12 12154人阅读 评论(0) 收藏 举报 分类: C#(14) 文章 ...
- Linq 多表连接查询join
在查询语言中,通常需要使用联接操作.在 LINQ 中,可以通过 join 子句实现联接操作.join 子句可以将来自不同源序列,并且在对象模型中没有直接关系(数据库表之间没有关系)的元素相关联,唯一的 ...
- LINQ TO SQL 中的join(转帖)
http://www.cnblogs.com/ASPNET2008/archive/2008/12/21/1358152.html join对于喜欢写SQL的朋友来说还是比较实用,也比较容易接受的东西 ...
- MySQL Left Join,Right Join
魂屁,东西发这里了关于Left Join,Right Join的 在讲MySQL的Join语法前还是先回顾一下联结的语法,呵呵,其实连我自己都忘得差不多了,那就大家一起温习吧(如果内容有错误或有疑问, ...
- 浅谈SQL Server中的三种物理连接操作(HASH JOIN MERGE JOIN NESTED LOOP)
简介 在SQL Server中,我们所常见的表与表之间的Inner Join,Outer Join都会被执行引擎根据所选的列,数据上是否有索引,所选数据的选择性转化为Loop Join,Merge J ...
- 左连接,右连接,内连接(left join ,right join,inner join)
浅谈左连接,右连接,内连接(left join ,right join,inner join) 这个问题一直困扰着我,每次遇到就打擦边球,不正面应对.今天索性把这几个连接搞懂了,写下这篇博客, 让跟我 ...
- SQL语句中LEFT JOIN、JOIN、INNER JOIN、RIGHT JOIN的区别?
w3school的一套sql教程: http://www.w3school.com.cn/sql/index.asp left join :左连接,返回左表中所有的记录以及右表中连接字段相等的记录.r ...
- join中级篇---------hash join & merge join & nested loop Join
嵌套循环连接(Nested Loop Join) 循环嵌套连接是最基本的连接,正如其名所示那样,需要进行循环嵌套,嵌套循环是三种方式中唯一支持不等式连接的方式,这种连接方式的过程可以简单的用下图展示: ...
- Mysql多表表关联查询 inner Join left join right join
Mysql多表表关联查询 inner Join left join right join
- SQL Left Join, Right Join, Inner Join, and Natural Join 各种Join小结
在SQL语言中,存在着各种Join,有Left Join, Right Join, Inner Join, and Natural Join等,对于初学者来说肯定一头雾水,都是神马跟神马啊,它们之间到 ...
随机推荐
- ios 一个正则表达式测试(只可输入中文、字母和数字)
一个正则表达式测试(只可输入中文.字母和数字) 在项目中碰到了正则表达式的运用,正则还是非常强大的,不管什么编程语言,基本上都可以用到.之前在用java时特别是对用户名或密码使用正则非常爽,写 脚本上 ...
- Python中的一些函数
1. 中文繁体/简体转换 下载 zh_wiki.py:https://github.com/skydark/nstools/blob/master/zhtools/zh_wiki.py 和 langc ...
- 单台centos7.3 虚拟机实现主从复制和哨兵集群
环境: centos7.3一台 部署图: 从服务器配置: slaveof 哨兵配置: port sentinel monitor m1 127.0.0.1 6379 2 sentinel monito ...
- JS:ES5数组基本操作
一.添加删除 push(): 尾部添加,返回数组 pop(): 尾部删除,返回删除项 unshift() : 头部添加,返回数组 shift() : 头部删除,返回删除项 二.插入.替换 万能spli ...
- hibernate的二级缓存----collection和query的二级缓存
collection二级缓存: 不使用集合的二级缓存时: 运行下面的代码: @Test public void testCollectionSecondLevelCache1(){ Departmen ...
- MySQL给一个字段递增赋值
https://blog.csdn.net/kriszhang/article/details/72125203 首先设置一个变量,初始值为0: set @r:=0; 1 然后更新表中对应的ID列: ...
- 解决mysql不能通过'/tmp/mysql.sock 连接的问题
解决方法:php标准配置正是通过'/tmp/mysql.sock',但一些mysql安装方法将mysql.sock放在/var/lib/mysql.sock或者其他地方,你可以通过修改/etc/my. ...
- JavaScript json和字符串互转
JavaScript内置json和字符串互转的函数JSON,不需要引入外部组件 JSON.stringify(obj)将JSON转为字符串. JSON.parse(string)将字符串转为JSON格 ...
- Spark Standalone Mode 单机启动Spark -- 分布式计算系统spark学习(一)
spark是个啥? Spark是一个通用的并行计算框架,由UCBerkeley的AMP实验室开发. Spark和Hadoop有什么不同呢? Spark是基于map reduce算法实现的分布式计算,拥 ...
- servlet实现多文件打包下载
当用户一次下载多个文件时.普通情况是,每下载一个文件,均要弹出一个下载的对话框.这给用户造成了非常大不便. 比較理想的情况是,用户选择多个文件后.server后端直接将多个文件打包为zip.以下贴出实 ...