LEFT OUTER JOIN in LINQ
如何在不使用
1 2 | List<JoinPair> innerFinal = (from l in lefts from r in rights where l.Key == r.Key select new JoinPair { LeftId = l.Id, RightId = r.Id}) |
但是对于左外部连接,我需要一个解决方案。我的有点像这样,但不起作用
1 2 3 4 5 | List< JoinPair> leftFinal = (from l in lefts from r in rights select new JoinPair { LeftId = l.Id, RightId = ((l.Key==r.Key) ? r.Id : 0 }) |
如果joinPair是类:
1 | public class JoinPair { long leftId; long rightId; } |
如:
101个Linq示例-左外部联接
1 2 3 4 5 | var q = from c in categories join p in products on c.Category equals p.Category into ps from p in ps.DefaultIfEmpty() select new { Category = c, ProductName = p == null ?"(No products)" : p.ProductName }; |
如果使用数据库驱动的LINQ提供程序,则可以编写一个可读性更高的左外部联接,如下所示:
1 2 | from maintable in Repo.T_Whatever from xxx in Repo.T_ANY_TABLE.Where(join condition).DefaultIfEmpty() |
如果省略
接受接受的答案:
1 2 3 | from c in categories join p in products on c equals p.Category into ps from p in ps.DefaultIfEmpty() |
这个语法非常混乱,当您想离开join多个表时,它是如何工作的还不清楚。< BR>
注
应该注意的是,
详细示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | var query2 = ( from users in Repo.T_User from mappings in Repo.T_User_Group .Where(mapping => mapping.USRGRP_USR == users.USR_ID) .DefaultIfEmpty() // <== makes join left join from groups in Repo.T_Group .Where(gruppe => gruppe.GRP_ID == mappings.USRGRP_GRP) .DefaultIfEmpty() // <== makes join left join // where users.USR_Name.Contains(keyword) // || mappings.USRGRP_USR.Equals(666) // || mappings.USRGRP_USR == 666 // || groups.Name.Contains(keyword) select new { UserId = users.USR_ID ,UserName = users.USR_User ,UserGroupId = groups.ID ,GroupName = groups.Name } ); var xy = (query2).ToList(); |
当与Linq2 SQL一起使用时,它将很好地转换为以下非常清晰的SQL查询:
1 2 3 4 5 6 7 8 9 10 11 12 | SELECT users.USR_ID AS UserId ,users.USR_User AS UserName ,groups.ID AS UserGroupId ,groups.Name AS GroupName FROM T_User AS users LEFT JOIN T_User_Group AS mappings ON mappings.USRGRP_USR = users.USR_ID LEFT JOIN T_Group AS groups ON groups.GRP_ID == mappings.USRGRP_GRP |
编辑:
也见"将SQL Server查询转换为LINQ查询"更复杂的例子。
另外,如果您是在linq-2-objects(而不是linq-2-sql)中执行此操作,则应该使用老式的方法(因为linq-to-sql会正确地将其转换为join操作,但是对于对象,此方法强制进行完全扫描,并且不利用索引搜索,为什么…):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var query2 = ( from users in Repo.T_Benutzer join mappings in Repo.T_Benutzer_Benutzergruppen on mappings.BEBG_BE equals users.BE_ID into tmpMapp join groups in Repo.T_Benutzergruppen on groups.ID equals mappings.BEBG_BG into tmpGroups from mappings in tmpMapp.DefaultIfEmpty() from groups in tmpGroups.DefaultIfEmpty() select new { UserId = users.BE_ID ,UserName = users.BE_User ,UserGroupId = mappings.BEBG_BG ,GroupName = groups.Name } ); |
使用lambda表达式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | db.Categories .GroupJoin( db.Products, Category => Category.CategoryId, Product => Product.CategoryId, (x, y) => new { Category = x, Products = y }) .SelectMany( xy => xy.Products.DefaultIfEmpty(), (x, y) => new { Category = x.Category, Product = y }) .Select(s => new { CategoryName = s.Category.Name, ProductName = s.Product.Name }) |
看看这个例子。此查询应该有效:
1 2 3 4 | var leftFinal = from left in lefts join right in rights on left equals right.Left into leftRights from leftRight in leftRights.DefaultIfEmpty() select new { LeftId = left.Id, RightId = left.Key==leftRight.Key ? leftRight.Id : 0 }; |
Now as an extension method:
1 2 3 4 5 6 7 8 9 10 11 12 | public static class LinqExt { public static IEnumerable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(this IEnumerable<TLeft> left, IEnumerable<TRight> right, Func<TLeft, TKey> leftKey, Func<TRight, TKey> rightKey, Func<TLeft, TRight, TResult> result) { return left.GroupJoin(right, leftKey, rightKey, (l, r) => new { l, r }) .SelectMany( o => o.r.DefaultIfEmpty(), (l, r) => new { lft= l.l, rght = r }) .Select(o => result.Invoke(o.lft, o.rght)); } } |
像通常使用join一样使用:
1 2 3 4 | var contents = list.LeftOuterJoin(list2, l => l.country, r => r.name, (l, r) => new { count = l.Count(), l.country, l.reason, r.people }) |
希望这能节省你一些时间。
通过扩展方法实现左外部联接可能看起来像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | public static IEnumerable<Result> LeftJoin<TOuter, TInner, TKey, Result>( this IEnumerable<TOuter> outer, IEnumerable<TInner> inner , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer) { if (outer == null) throw new ArgumentException("outer"); if (inner == null) throw new ArgumentException("inner"); if (outerKeySelector == null) throw new ArgumentException("outerKeySelector"); if (innerKeySelector == null) throw new ArgumentException("innerKeySelector"); if (resultSelector == null) throw new ArgumentException("resultSelector"); return LeftJoinImpl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer ?? EqualityComparer<TKey>.Default); } static IEnumerable<Result> LeftJoinImpl<TOuter, TInner, TKey, Result>( IEnumerable<TOuter> outer, IEnumerable<TInner> inner , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer) { var innerLookup = inner.ToLookup(innerKeySelector, comparer); foreach (var outerElment in outer) { var outerKey = outerKeySelector(outerElment); var innerElements = innerLookup[outerKey]; if (innerElements.Any()) foreach (var innerElement in innerElements) yield return resultSelector(outerElment, innerElement); else yield return resultSelector(outerElment, default(TInner)); } } |
结果选择器必须处理空元素。外汇。
1 2 3 4 5 6 7 8 9 10 11 | static void Main(string[] args) { var inner = new[] { Tuple.Create(1,"1"), Tuple.Create(2,"2"), Tuple.Create(3,"3") }; var outer = new[] { Tuple.Create(1,"11"), Tuple.Create(2,"22") }; var res = outer.LeftJoin(inner, item => item.Item1, item => item.Item1, (it1, it2) => new { Key = it1.Item1, V1 = it1.Item2, V2 = it2 != null ? it2.Item2 : default(string) }); foreach (var item in res) Console.WriteLine(string.Format("{0}, {1}, {2}", item.Key, item.V1, item.V2)); } |
看看这个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | class Person { public int ID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Phone { get; set; } } class Pet { public string Name { get; set; } public Person Owner { get; set; } } public static void LeftOuterJoinExample() { Person magnus = new Person {ID = 1, FirstName ="Magnus", LastName ="Hedlund"}; Person terry = new Person {ID = 2, FirstName ="Terry", LastName ="Adams"}; Person charlotte = new Person {ID = 3, FirstName ="Charlotte", LastName ="Weiss"}; Person arlene = new Person {ID = 4, FirstName ="Arlene", LastName ="Huff"}; Pet barley = new Pet {Name ="Barley", Owner = terry}; Pet boots = new Pet {Name ="Boots", Owner = terry}; Pet whiskers = new Pet {Name ="Whiskers", Owner = charlotte}; Pet bluemoon = new Pet {Name ="Blue Moon", Owner = terry}; Pet daisy = new Pet {Name ="Daisy", Owner = magnus}; // Create two lists. List<Person> people = new List<Person> {magnus, terry, charlotte, arlene}; List<Pet> pets = new List<Pet> {barley, boots, whiskers, bluemoon, daisy}; var query = from person in people where person.ID == 4 join pet in pets on person equals pet.Owner into personpets from petOrNull in personpets.DefaultIfEmpty() select new { Person=person, Pet = petOrNull}; foreach (var v in query ) { Console.WriteLine("{0,-15}{1}", v.Person.FirstName +":", (v.Pet == null ?"Does not Exist" : v.Pet.Name)); } } // This code produces the following output: // // Magnus: Daisy // Terry: Barley // Terry: Boots // Terry: Blue Moon // Charlotte: Whiskers // Arlene: |
现在,即使那个元素
这是参考资料
如何:执行左侧外部连接(C编程指南)
这是一般形式(如其他答案中所述)
1 2 3 4 5 | var c = from a in alpha join b in beta on b.field1 equals a.field1 into b_temp from b_value in b_temp.DefaultIfEmpty() select new { Alpha = a, Beta = b_value }; |
不过,这里有一个解释,我希望能澄清这到底意味着什么!
1 | join b in beta on b.field1 equals a.field1 into b_temp |
基本上创建一个单独的结果集b_temp,它有效地为右侧的条目(b'中的条目)包含空的"行"。
然后下一行:
1 | from b_value in b_temp.DefaultIfEmpty() |
..迭代该结果集,为右侧的"row"设置默认的空值,并将右侧行join的结果设置为"b_value"的值(即,如果有匹配的记录,则为右侧的值,否则为"null")。
现在,如果右侧是单独的LINQ查询的结果,它将由匿名类型组成,匿名类型只能是"something"或"null"。但是,如果它是一个可枚举的(例如,一个列表-其中myObjectB是一个具有2个字段的类),那么可以具体说明其属性使用的默认"空"值是什么:
1 2 3 4 5 |
这样可以确保"b"本身不为空(但它的属性可以为空,使用您指定的默认空值),这允许您检查b_值的属性,而不必为b_值获取空引用异常。请注意,对于可为空的日期时间,类型为(日期时间?)例如,"nullable datetime"必须在"defaultifempty"的规范中指定为空的"type"(这也适用于不可"native"为空的类型,例如double、float)。
您可以通过简单地链接上述语法来执行多个左外部联接。
下面是一个例子,如果您需要连接两个以上的表:
1 2 3 4 5 6 7 | from d in context.dc_tpatient_bookingd join bookingm in context.dc_tpatient_bookingm on d.bookingid equals bookingm.bookingid into bookingmGroup from m in bookingmGroup.DefaultIfEmpty() join patient in dc_tpatient on m.prid equals patient.prid into patientGroup from p in patientGroup.DefaultIfEmpty() |
参考:https://stackoverflow.com/a/17142392/2343
有三张表:人、学校和人-学校,将人与他们学习的学校联系起来。表中没有对ID=6的人的引用。但是,id=6的人出现在结果lef-joined网格中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | List<Person> persons = new List<Person> { new Person { id = 1, name ="Alex", phone ="4235234" }, new Person { id = 2, name ="Bob", phone ="0014352" }, new Person { id = 3, name ="Sam", phone ="1345" }, new Person { id = 4, name ="Den", phone ="3453452" }, new Person { id = 5, name ="Alen", phone ="0353012" }, new Person { id = 6, name ="Simon", phone ="0353012" } }; List<School> schools = new List<School> { new School { id = 1, name ="Saint. John's school <div class="suo-content">[collapse title=""]<ul><li>虽然这可能是问题的答案,但请对您的答案提供一些解释:)</li></ul>[/collapse]</div><hr><P>扩展方法的工作方式与使用联接语法的左联接类似</P>[cc lang="csharp"]public static class LinQExtensions { public static IEnumerable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>( this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector) { return outer.GroupJoin( inner, outerKeySelector, innerKeySelector, (outerElement, innerElements) => resultSelector(outerElement, innerElements.FirstOrDefault())); } } |
刚刚在.NET核心中编写了它,它似乎按预期工作。
小测验:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | var Ids = new List<int> { 1, 2, 3, 4}; var items = new List<Tuple<int, string>> { new Tuple<int, string>(1,"a"), new Tuple<int, string>(2,"b"), new Tuple<int, string>(4,"d"), new Tuple<int, string>(5,"e"), }; var result = Ids.LeftJoin( items, id => id, item => item.Item1, (id, item) => item ?? new Tuple<int, string>(id,"not found")); result.ToList() Count = 4 [0]: {(1, a)} [1]: {(2, b)} [2]: {(3, not found)} [3]: {(4, d)} |
这是一个与内部和左侧外部联接的LINQ语法比较的SQL语法。左外部联接:
http://www.ozkary.com/2011/07/linq-to-entity-inner-and-left-joins.html
"下面的示例是在产品和类别之间进行组联接。这基本上是左连接。即使类别表为空,into表达式也返回数据。要访问类别表的属性,现在必须通过在catlist.defaultifempty()语句中添加fromcl从可枚举结果中进行选择。
在LINQ C中执行左外部联接#//执行左外部联接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | class Person { public string FirstName { get; set; } public string LastName { get; set; } } class Child { public string Name { get; set; } public Person Owner { get; set; } } public class JoinTest { public static void LeftOuterJoinExample() { Person magnus = new Person { FirstName ="Magnus", LastName ="Hedlund" }; Person terry = new Person { FirstName ="Terry", LastName ="Adams" }; Person charlotte = new Person { FirstName ="Charlotte", LastName ="Weiss" }; Person arlene = new Person { FirstName ="Arlene", LastName ="Huff" }; Child barley = new Child { Name ="Barley", Owner = terry }; Child boots = new Child { Name ="Boots", Owner = terry }; Child whiskers = new Child { Name ="Whiskers", Owner = charlotte }; Child bluemoon = new Child { Name ="Blue Moon", Owner = terry }; Child daisy = new Child { Name ="Daisy", Owner = magnus }; // Create two lists. List<Person> people = new List<Person> { magnus, terry, charlotte, arlene }; List<Child> childs = new List<Child> { barley, boots, whiskers, bluemoon, daisy }; var query = from person in people join child in childs on person equals child.Owner into gj from subpet in gj.DefaultIfEmpty() select new { person.FirstName, ChildName = subpet!=null? subpet.Name:"No Child" }; // PetName = subpet?.Name ?? String.Empty }; foreach (var v in query) { Console.WriteLine($"{v.FirstName +":",-25}{v.ChildName}"); } } // This code produces the following output: // // Magnus: Daisy // Terry: Barley // Terry: Boots // Terry: Blue Moon // Charlotte: Whiskers // Arlene: No Child |
https://dotnetwithhamid.blogspot.in网站/
下面是使用方法语法相当容易理解的版本:
1 2 3 4 5 |
我想补充一点,如果您得到morelinq扩展,现在就可以同时支持同质和异构的左连接了
http://morelinq.github.io/2.8/ref/api/html/overload_morelinq_moreenumerable_leftjoin.htm(http://morelinq.github.io/2.8/ref/api/html/overload_morelinq_moreenumerable
例子:
1 2 3 4 5 6 7 8 | //Pretend a ClientCompany object and an Employee object both have a ClientCompanyID key on them return DataContext.ClientCompany .LeftJoin(DataContext.Employees, //Table being joined company => company.ClientCompanyID, //First key employee => employee.ClientCompanyID, //Second Key company => new {company, employee = (Employee)null}, //Result selector when there isn't a match (company, employee) => new { company, employee }); //Result selector when there is a match |
编辑:
回想起来,这可能是可行的,但它将IQueryable转换为IEnumerable,因为MoreLinq不将查询转换为SQL。
您可以使用groupjoin,如下所述:https://stackoverflow.com/a/24273804/4251433
这将确保它作为一个iQueryable保持,以防您以后需要对其进行进一步的逻辑操作。
如果需要联接和过滤某些内容,可以在联接之外进行。可以在创建集合后进行筛选。
在本例中,如果在联接条件中执行此操作,则会减少返回的行。
采用三元条件dOCx1〔0〕。
如果对象是
null (因此不匹配),则返回? 之后的内容。在这种情况下,__ 。否则,返回
: 和n.MonDayNote 之后的内容。
感谢其他的贡献者,这是我开始自己的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | var schedLocations = (from f in db.RAMS_REVENUE_LOCATIONS join n in db.RAMS_LOCATION_PLANNED_MANNING on f.revenueCenterID equals n.revenueCenterID into lm from n in lm.DefaultIfEmpty() join r in db.RAMS_LOCATION_SCHED_NOTE on f.revenueCenterID equals r.revenueCenterID into locnotes from r in locnotes.DefaultIfEmpty() where f.LocID == nLocID && f.In_Use == true && f.revenueCenterID > 1000 orderby f.Areano ascending, f.Locname ascending select new { Facname = f.Locname, f.Areano, f.revenueCenterID, f.Locabbrev, // MonNote = n == null ?"__" : n.MonDayNote, MonNote = n == null ?"__" : n.MonDayNote, TueNote = n == null ?"__" : n.TueDayNote, WedNote = n == null ?"__" : n.WedDayNote, ThuNote = n == null ?"__" : n.ThuDayNote, FriNote = n == null ?"__" : n.FriDayNote, SatNote = n == null ?"__" : n.SatDayNote, SunNote = n == null ?"__" : n.SunDayNote, MonEmpNbr = n == null ? 0 : n.MonEmpNbr, TueEmpNbr = n == null ? 0 : n.TueEmpNbr, WedEmpNbr = n == null ? 0 : n.WedEmpNbr, ThuEmpNbr = n == null ? 0 : n.ThuEmpNbr, FriEmpNbr = n == null ? 0 : n.FriEmpNbr, SatEmpNbr = n == null ? 0 : n.SatEmpNbr, SunEmpNbr = n == null ? 0 : n.SunEmpNbr, SchedMondayDate = n == null ? dMon : n.MondaySchedDate, LocNotes = r == null ?"Notes: N/A" : r.LocationNote }).ToList(); Func<int, string> LambdaManning = (x) => { return x == 0 ?"" :"Manning:" + x.ToString(); }; DataTable dt_ScheduleMaster = PsuedoSchedule.Tables["ScheduleMasterWithNotes"]; var schedLocations2 = schedLocations.Where(x => x.SchedMondayDate == dMon); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | (from a in db.Assignments join b in db.Deliveryboys on a.AssignTo equals b.EmployeeId //from d in eGroup.DefaultIfEmpty() join c in db.Deliveryboys on a.DeliverTo equals c.EmployeeId into eGroup2 from e in eGroup2.DefaultIfEmpty() where (a.Collected == false) select new { OrderId = a.OrderId, DeliveryBoyID = a.AssignTo, AssignedBoyName = b.Name, Assigndate = a.Assigndate, Collected = a.Collected, CollectedDate = a.CollectedDate, CollectionBagNo = a.CollectionBagNo, DeliverTo = e == null ?"Null" : e.Name, DeliverDate = a.DeliverDate, DeliverBagNo = a.DeliverBagNo, Delivered = a.Delivered }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | class Program { List<Employee> listOfEmp = new List<Employee>(); List<Department> listOfDepart = new List<Department>(); public Program() { listOfDepart = new List<Department>(){ new Department { Id = 1, DeptName ="DEV" }, new Department { Id = 2, DeptName ="QA" }, new Department { Id = 3, DeptName ="BUILD" }, new Department { Id = 4, DeptName ="SIT" } }; listOfEmp = new List<Employee>(){ new Employee { Empid = 1, Name ="Manikandan",DepartmentId=1 }, new Employee { Empid = 2, Name ="Manoj" ,DepartmentId=1}, new Employee { Empid = 3, Name ="Yokesh" ,DepartmentId=0}, new Employee { Empid = 3, Name ="Purusotham",DepartmentId=0} }; } static void Main(string[] args) { Program ob = new Program(); ob.LeftJoin(); Console.ReadLine(); } private void LeftJoin() { listOfEmp.GroupJoin(listOfDepart.DefaultIfEmpty(), x => x.DepartmentId, y => y.Id, (x, y) => new { EmpId = x.Empid, EmpName = x.Name, Dpt = y.FirstOrDefault() != null ? y.FirstOrDefault().DeptName : null }).ToList().ForEach (z => { Console.WriteLine("Empid:{0} EmpName:{1} Dept:{2}", z.EmpId, z.EmpName, z.Dpt); }); } } class Employee { public int Empid { get; set; } public string Name { get; set; } public int DepartmentId { get; set; } } class Department { public int Id { get; set; } public string DeptName { get; set; } } |
产量
左外部联接的简单解决方案:
1 2 3 | var setA = context.SetA; var setB = context.SetB.Select(st=>st.Id).Distinct().ToList(); var leftOuter = setA.Where(stA=> !setB.Contains(stA.Id)); |
笔记:
- 为了提高性能,可以将setb转换为字典(如果这样做了,则必须更改此:!setb.contains(sta.id))或哈希集
- 当涉及多个字段时,可以使用set操作和实现以下内容的类来实现:ieQualityComparer