sql中的連接
sql中的表連接有inner join,left join(left outer join),right join(right outer join),full join(full outer join),cross join
在此基礎上我們能擴展出 left excluding join,right excluding join,full outer excluding join
注:left join是left outer join 的簡寫,即左連接和左外連接是一樣的
首先定義兩個比較經典的表
學生信息表和選課表
student
studentId name sex 1 小明 男 2 小黃 男 3 小紅 女 4 小楊 男
course
studentId courseName 1 數學 1 語文 1 英語 2 數學 2 語文 2 英語 3 數學 3 語文 3 英語 5 數學 5 語文 5 英語
這兩張表其實並不規范,course的studentId其實是一個外鍵,對應student的studentId,所以course的studentId不應該有5,不過為了測試方便,暫且這么寫
內連接(inner join)
select s.* ,c.courseName from student s inner join course c on s.studentId=c.studentId
結果
studentId name sex courseName 1 小明 男 數學 1 小明 男 語文 1 小明 男 英語 2 小黃 男 數學 2 小黃 男 語文 2 小黃 男 英語 3 小紅 女 數學 3 小紅 女 語文 3 小紅 女 英語
左連接(left join)
select s.* ,c.courseName from student s left join course c on s.studentId=c.studentId
結果
studentId name sex courseName 1 小明 男 數學 1 小明 男 語文 1 小明 男 英語 2 小黃 男 數學 2 小黃 男 語文 2 小黃 男 英語 3 小紅 女 數學 3 小紅 女 語文 3 小紅 女 英語 4 小楊 男 NULL
右連接
select s.* ,c.courseName from student s right join course c on s.studentId=c.studentId
結果
studentId name sex courseName 1 小明 男 數學 1 小明 男 語文 1 小明 男 英語 2 小黃 男 數學 2 小黃 男 語文 2 小黃 男 英語 3 小紅 女 數學 3 小紅 女 語文 3 小紅 女 英語 NULL NULL NULL 數學 NULL NULL NULL 語文 NULL NULL NULL 英語
全連接
select s.* ,c.courseName from student s full join course c on s.studentId=c.studentId
結果
studentId name sex courseName 1 小明 男 數學 1 小明 男 語文 1 小明 男 英語 2 小黃 男 數學 2 小黃 男 語文 2 小黃 男 英語 3 小紅 女 數學 3 小紅 女 語文 3 小紅 女 英語 4 小楊 男 NULL NULL NULL NULL 數學 NULL NULL NULL 語文 NULL NULL NULL 英語
左不包含連接(left excluding join)
select s.* ,c.courseName from student s left join course c on s.studentId=c.studentId where c.studentId is null
結果
studentId name sex courseName 4 小楊 男 NULL
右不包含連接(right excluding join)
select s.* ,c.courseName from student s right join course c on s.studentId=c.studentId where s.studentId is null
結果
studentId name sex courseName NULL NULL NULL 數學 NULL NULL NULL 語文 NULL NULL NULL 英語
全不包含連接(Full outer excluding join)
select s.* ,c.courseName from student s full join course c on s.studentId=c.studentId where s.studentId is null or c.studentId is null
結果
studentId name sex courseName 4 小楊 男 NULL NULL NULL NULL 數學 NULL NULL NULL 語文 NULL NULL NULL 英語
笛卡兒積(cross join)
select s.* ,c.courseName from student s cross join course c
結果

studentId name sex courseName 1 小明 男 數學 1 小明 男 語文 1 小明 男 英語 1 小明 男 數學 1 小明 男 語文 1 小明 男 英語 1 小明 男 數學 1 小明 男 語文 1 小明 男 英語 1 小明 男 數學 1 小明 男 語文 1 小明 男 英語 2 小黃 男 數學 2 小黃 男 語文 2 小黃 男 英語 2 小黃 男 數學 2 小黃 男 語文 2 小黃 男 英語 2 小黃 男 數學 2 小黃 男 語文 2 小黃 男 英語 2 小黃 男 數學 2 小黃 男 語文 2 小黃 男 英語 3 小紅 女 數學 3 小紅 女 語文 3 小紅 女 英語 3 小紅 女 數學 3 小紅 女 語文 3 小紅 女 英語 3 小紅 女 數學 3 小紅 女 語文 3 小紅 女 英語 3 小紅 女 數學 3 小紅 女 語文 3 小紅 女 英語 4 小楊 男 數學 4 小楊 男 語文 4 小楊 男 英語 4 小楊 男 數學 4 小楊 男 語文 4 小楊 男 英語 4 小楊 男 數學 4 小楊 男 語文 4 小楊 男 英語 4 小楊 男 數學 4 小楊 男 語文 4 小楊 男 英語
兩個個經典sql問題的解法
一、取出沒有選課的學生的信息
方法一:利用left excluding join
select s.* from student s left join course c on s.studentId=c.studentId where c.studentId is null
結果
studentId name sex 4 小楊 男
方法二:利用exists
思路:先找到有選課的學生的信息然后通過exists或not exists來取出想要的數據
select * from student st where not exists( select s.* ,c.courseName from student s inner join course c on s.studentId=c.studentId where st.studentId=s.studentId )
結果跟方法一的一樣
二、取出有選課的學生的信息
select * from student st where exists( select s.* ,c.courseName from student s inner join course c on s.studentId=c.studentId where st.studentId=s.studentId )
結果
studentId name sex 1 小明 男 2 小黃 男 3 小紅 女
Linq 中的連接
在linq中同樣能實現上述sql的連接操作

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data; namespace LinqJoinTest { class Program { static void Main(string[] args) { DataTable student = GetStudent(); DataTable course = GetCourse(); Console.WriteLine("內連接"); IEnumerable<ResultModel> result = InnerJoin(student, course); foreach(ResultModel item in result) { Console.WriteLine(string.Format("{0},{1},{2},{3}", item.id, item.name, item.sex, item.course)); } Console.WriteLine("左連接"); result = LeftJoin(student, course); foreach (ResultModel item in result) { Console.WriteLine(string.Format("{0},{1},{2},{3}", item.id, item.name, item.sex, item.course)); } Console.WriteLine("右連接"); result = RightJoin(student, course); foreach (ResultModel item in result) { Console.WriteLine(string.Format("{0},{1},{2},{3}", item.id, item.name, item.sex, item.course)); } Console.WriteLine("全連接"); result = AllJoin(student, course); foreach (ResultModel item in result) { Console.WriteLine(string.Format("{0},{1},{2},{3}", item.id, item.name, item.sex, item.course)); } Console.WriteLine("左不包含連接"); result = LeftOuterJoin(student, course); foreach (ResultModel item in result) { Console.WriteLine(string.Format("{0},{1},{2},{3}", item.id, item.name, item.sex, item.course)); } Console.WriteLine("右不包含連接"); result = RightOuterJoin(student, course); foreach (ResultModel item in result) { Console.WriteLine(string.Format("{0},{1},{2},{3}", item.id, item.name, item.sex, item.course)); } Console.WriteLine("全不包含連接"); result = AllOuterJoin(student, course); foreach (ResultModel item in result) { Console.WriteLine(string.Format("{0},{1},{2},{3}", item.id, item.name, item.sex, item.course)); } Console.ReadKey(); } public static DataTable GetStudent() { DataTable student = new DataTable(); student.Columns.Add("studentId"); student.Columns.Add("name"); student.Columns.Add("sex"); student.Rows.Add(new object[] { "1", "小明", "男" }); student.Rows.Add(new object[] { "2", "小黃", "男" }); student.Rows.Add(new object[] { "3", "小紅", "女" }); student.Rows.Add(new object[] { "4", "小楊", "男" }); return student; } public static DataTable GetCourse() { DataTable course = new DataTable(); course.Columns.Add("studentId"); course.Columns.Add("courseName"); course.Rows.Add(new object[] { "1", "數學" }); course.Rows.Add(new object[] { "1", "英語" }); course.Rows.Add(new object[] { "1", "語文" }); course.Rows.Add(new object[] { "2", "數學" }); course.Rows.Add(new object[] { "2", "英語" }); course.Rows.Add(new object[] { "2", "語文" }); course.Rows.Add(new object[] { "3", "數學" }); course.Rows.Add(new object[] { "3", "英語" }); course.Rows.Add(new object[] { "3", "語文" }); course.Rows.Add(new object[] { "5", "數學" }); course.Rows.Add(new object[] { "5", "英語" }); course.Rows.Add(new object[] { "5", "語文" }); return course; } /// <summary> /// 內連接 /// </summary> /// <param name="student"></param> /// <param name="course"></param> /// <returns></returns> public static IEnumerable<ResultModel> InnerJoin(DataTable student, DataTable course) { //Lambda表達式 var result = from s in student.Select() join c in course.Select() on s["studentId"].ToString() equals c["studentId"].ToString() select new ResultModel { id = s["studentId"].ToString(), name = s["name"].ToString(), sex = s["sex"].ToString(), course = c["courseName"].ToString() }; //查詢表達式語法 result = student.Select() .Join(course.Select(), s => s["studentId"].ToString(), c => c["studentId"].ToString(), (s, c) => new ResultModel { id = s["studentId"].ToString(), name = s["name"].ToString(), sex = s["sex"].ToString(), course = c["courseName"].ToString() }); return result; } /// <summary> /// 左連接(左外連接) linq中只有左連接,右連接只要把數據集合順序倒轉就行了 /// </summary> /// <param name="student"></param> /// <param name="course"></param> /// <returns></returns> public static IEnumerable<ResultModel> LeftJoin(DataTable student, DataTable course) { //Lambda表達式 var result = from s in student.Select() join c in course.Select() on s["studentId"].ToString() equals c["studentId"].ToString() into temple from t in temple.DefaultIfEmpty() select new ResultModel { id = s["studentId"].ToString(), name = s["name"].ToString(), sex = s["sex"].ToString(), course = t==null?"Null":t["courseName"].ToString() }; //查詢表達式語法 result = student.Select().GroupJoin(course.Select(), s => s["studentId"].ToString(), c => c["studentId"].ToString(), (s, c) => new { s, c }).SelectMany(g => g.c.DefaultIfEmpty(), (item, c) => new ResultModel { id = item.s["studentId"].ToString(), name = item.s["name"].ToString(), sex = item.s["sex"].ToString(), course = c == null ? "Null" : c["courseName"].ToString() }); return result; } /// <summary> /// 右連接(右外連接) /// </summary> /// <param name="student"></param> /// <param name="course"></param> /// <returns></returns> public static IEnumerable<ResultModel> RightJoin(DataTable student, DataTable course) { //Lambda表達式 var result = from c in course.Select() join s in student.Select() on c["studentId"].ToString() equals s["studentId"].ToString() into temple from t in temple.DefaultIfEmpty() select new ResultModel { id = t == null ? "Null" : t["studentId"].ToString(), name = t == null ? "Null" : t["name"].ToString(), sex = t == null ? "Null" : t["sex"].ToString(), course = c["courseName"].ToString() }; //查詢表達式語法 result = course.Select().GroupJoin(student.Select(), s => s["studentId"].ToString(), c => c["studentId"].ToString(), (s, c) => new { s, c }).SelectMany(g => g.c.DefaultIfEmpty(), (item, c) => new ResultModel { id = c == null ? "Null" : c["studentId"].ToString(), name = c == null ? "Null" : c["name"].ToString(), sex = c == null ? "Null" : c["sex"].ToString(), course =item.s["courseName"].ToString() }); return result; } /// <summary> /// 全連接(全外連接) /// </summary> /// <param name="student"></param> /// <param name="course"></param> /// <returns></returns> public static IEnumerable<ResultModel> AllJoin(DataTable student, DataTable course) { IEnumerable<ResultModel> left = LeftJoin(student, course); IEnumerable<ResultModel> right = RightJoin(student, course); //比較器 IEqualityComparer<ResultModel> ec = new EntityComparer(); return left.Union(right, ec); } /// <summary> /// 左不包含連接 /// </summary> /// <param name="student"></param> /// <param name="course"></param> /// <returns></returns> public static IEnumerable<ResultModel> LeftOuterJoin(DataTable student, DataTable course) { //Lambda表達式 var result = from s in student.Select() join c in course.Select() on s["studentId"].ToString() equals c["studentId"].ToString() into temple from t in temple.DefaultIfEmpty() where t==null select new ResultModel { id = s["studentId"].ToString(), name = s["name"].ToString(), sex = s["sex"].ToString(), course ="Null" }; //查詢表達式語法 result = student.Select().GroupJoin(course.Select(), s => s["studentId"].ToString(), c => c["studentId"].ToString(), (s, c) => new { s, c }) .SelectMany(g => g.c.DefaultIfEmpty(), (item, c) => new { item,c}).Where(item => item.c== null) .Select(item=>new ResultModel { id = item.item.s["studentId"].ToString(), name = item.item.s["name"].ToString(), sex = item.item.s["sex"].ToString(), course ="Null" }); return result; } /// <summary> /// 右不包含連接 /// </summary> /// <param name="student"></param> /// <param name="course"></param> /// <returns></returns> public static IEnumerable<ResultModel> RightOuterJoin(DataTable student, DataTable course) { //Lambda表達式 var result = from c in course.Select() join s in student.Select() on c["studentId"].ToString() equals s["studentId"].ToString() into temple from t in temple.DefaultIfEmpty() where t==null select new ResultModel { id = "Null", name = "Null", sex = "Null", course = c["courseName"].ToString() }; //查詢表達式語法 result = course.Select().GroupJoin(student.Select(), s => s["studentId"].ToString(), c => c["studentId"].ToString(), (s, c) => new { s, c }).SelectMany(g => g.c.DefaultIfEmpty(), (item, c) => new { item, c }).Where(item=>item.c==null) .Select(item => new ResultModel { id ="Null", name ="Null", sex = "Null" , course = item.item.s["courseName"].ToString() }); return result; } /// <summary> /// 全不包含連接 /// </summary> /// <param name="student"></param> /// <param name="course"></param> /// <returns></returns> public static IEnumerable<ResultModel> AllOuterJoin(DataTable student, DataTable course) { IEnumerable<ResultModel> left = LeftOuterJoin(student, course); IEnumerable<ResultModel> right = RightOuterJoin(student, course); return left.Union(right); } /// <summary> /// 交叉連接(笛卡爾積) /// </summary> /// <param name="student"></param> /// <param name="course"></param> /// <returns></returns> public static IEnumerable<ResultModel> CrossJoin(DataTable student, DataTable course) { //Lambda表達式 var result = from s in student.Select() from c in course.Select() select new ResultModel { id = s["studentId"].ToString(), name = s["name"].ToString(), sex = s["sex"].ToString(), course = c["courseName"].ToString() }; //查詢表達式語法 result = student.Select() .SelectMany(c=>course.Select(), (s, c) => new ResultModel { id = s["studentId"].ToString(), name = s["name"].ToString(), sex = s["sex"].ToString(), course = c["courseName"].ToString() }); return result; } } public class ResultModel { public string id { get; set; } public string name { get; set; } public string sex { get; set; } public string course { get; set; } } public class EntityComparer : IEqualityComparer<ResultModel> { public bool Equals(ResultModel a, ResultModel b) { if (Object.ReferenceEquals(a, b)) return true; if (Object.ReferenceEquals(a, null) || Object.ReferenceEquals(b, null)) return false; return a.id == b.id && a.name == b.name && a.sex == b.sex&&a.course==b.course; } public int GetHashCode(ResultModel a) { if (Object.ReferenceEquals(a, null)) return 0; int hashId = a.id == null ? 0 : a.id.GetHashCode(); int hashName = a.name == null ? 0 : a.id.GetHashCode(); int hashSex = a.sex == null ? 0 : a.sex.GetHashCode(); int hashCourse = a.course == null ? 0 : a.course.GetHashCode(); return hashId ^ hashName ^ hashSex ^ hashCourse; } } }