LINQtoEntitiesEquivalenttoSQL"TOP(n)WITHTIES",我遇到了一些行不通的事情。我知道这个问题之前已经被问过并且有一个可以接受的答案,但它并不像领带那样有效。使用GroupBy()的解决方案不会给出TOP(3)WITHTIES预期结果,考虑到由{322110}组成的数据集,结果集将是{32211},应该是{322}使用以下示例数据(取自此问题):CREATETABLEPerson(Idintprimarykey,Namenvarchar(50),Scorefloat)INSERTINTOPersonVALUES(1,'Tom',8.9)INSERTINTOPersonVALUES(2,'Jerry',8.9)INSERTINTOPersonVALUES(3,'Sharti',7)INSERTINTOPersonVALUES(4,'Mamuzi',9)INSERTINTOPersonVALUES(5,'Kamala',9)传统的OrderByDescending(p=>p.Score).Take(3)将导致:Mamuzi、Kamala和Tom(或Jerry)之一应该包括我所知道的没有内置等效项,我找到了实现它的方法。我不知道这是否是最好的方法并且愿意接受其他解决方案。varquery=(fromqinlist.OrderByDescending(s=>s.Score).Take(3).Select(s=>s.Score).Distinct()fromiinlistwhereq==i.Score选择我).ToList();编辑:@Zefnus我不确定你想要它的顺序,但要更改顺序,你可以在selecti和ToList()之间放置一个OrderBy(s=>s.Score)我没办法检查我的sql语句linq子句将产生。但我认为你的答案要好得多。你的问题也很好。我从来没有想过linq中的关系。;)基本上它只是从第一个列表中获取前3个分数并将它们与整个列表进行比较,我只得到那些等于第一个列表分数的分数。不要在接触数据库的任何东西上使用IEnumerable!LinqToSql和LinqToEntities的解决方案不应使用IEnumerable。您当前的自我回答将导致从数据库中选择每个人,然后使用LinqToObjects在内存中查询。要创建一个转换为SQL并由数据库执行的解决方案,您必须使用IQueryable和Expressions。publicstaticclassQueryableExtensions{publicstaticIQueryableTopWithTies(thisIQueryablesource,Expression>topBy,inttopCount){if(source==null)thrownewArgumentNullException("source");}如果(topBy==null)抛出新的ArgumentNullException("topBy");如果(topCountmi.Name=="Max"&&mi.GetParameters().Length==1&&mi.IsGenericMethod).MakeGenericMethod(typeof(TComparand));varlessThanOrEqualToMaxTopValue=Expression.Lambda>(Expression.LessThanOrEqual(topBy.Body,Expression.Call(queryableMaxMethod,topValues.Expression)),new[]{topBy.Parameters.Single()});vartopNRowsWithTies=source.Where(lessThanOrEqualToMaxTopValue).OrderBy(topBy);返回topNRowsWithTies;}publicstaticIQueryableTopWithTiesDescending(thisIQueryablesource,Expression>topBy,inttopCount){if(source==null)thrownewArgumentNullException("source");如果(topBy==null)抛出新的ArgumentNullException("topBy");如果(topCountmi.Name=="Min"&&mi.GetParameters().Length==1&&mi.IsGenericMethod).MakeGenericMethod(typeof(TComparand));vargreaterThanOrEqualToMinTopValue=Expression.Lambda>(Expression.GreaterThanOrEqual(topBy.Body,Expression.Call(queryableMinMethod,topValues.Expression)),new[]{topBy.Parameters.Single()});vartopNRowsWithTies=source.Where(greaterThanOrEqualToMinTopValue).OrderByDescending(topBy);返回topNRowsWithTies;这将创建以下形式的查询:SELECT[t0].[Id],[t0].[Name],[t0].[Score]FROM[Person]AS[t0]WHERE[t0].[Score]>=((SELECTMIN([t2].[Score])FROM(SELECTTOP(3)[t1].[Score]FROM[Person]AS[t1]ORDERBY[t1].[Score]DESC)AS[t2]))ORDERBY[t0].[Score]DESC这个查询只比基线查询差50%:SELECTTOP(3)WITHTIES[t0].[Id],[t0].[Name],[t0].[Score]FROM[Person]AS[t0]ORDERBY[t0].[Score]desc使用由原始5条记录和另外10000条记录组成的数据集,所有记录的分数都低于原始记录,它们是或多或少是瞬时的(小于20毫秒)IEnumerable方法用了整整2分钟!如果表达式构建和反射看起来很糟糕,同样可以使用连接来实现:如果(topBy==null)抛出新的ArgumentNullException("topBy");如果(topCount值,topBy,(x,行)=>行);返回topNRowsWithTies.OrderByDescending(topBy);}使用以下查询作为结果(性能大致相同):SELECT[t3].[Id],[t3].[Name],[t3].[Score]FROM(SELECTDISTINCT[t1].[Score]FROM(SELECTTOP(3)[t0].[Score]FROM[Person]AS[t0]ORDERBY[t0].[Score]DESC)AS[t1])AS[t2]INNERJOIN[Person]AS[t3]ON[t2].[Score]=[t3].[Score]ORDERBY[t3].[Score]DESC另一种解决方案——可能不如其他解决方案有效——是获得TOP(3)分数并获得TOP(3)包含分值线。我们可以按如下方式使用Contains();orderedPerson=datamodel.People.OrderByDescending(p=>p.Score);topPeopleList=(frompinorderedPersonlettopNPersonScores=orderedPerson.Take(n).Select(p=>p.Score).Distinct()其中topNPersonScores.Contains(p.Score)选择p).ToList();这个实现有什么好处呢,它的扩展方法TopWithTies()很容易实现;publicstaticIEnumerableTopWithTies(thisIEnumerableenumerable,Funcselector,intn){IEnumerableorderedEnumerable=enumerable.OrderByDescending(selector);return(frompinorderedEnumerablelettopNValues=orderedEnumerable.Take(n).Select(selector).Distinct()wheretopNValues.Contains(selector(p))selectp);我想也许你可以这样做:OrderByDescending(p=>p.Score).Skip(2).Take(1)计算这个元素的出现次数,然后:OrderByDescending(p=>p.Score).Take(2+“带有第三个元素出现次数的选择”)我认为这可能有效;)这只是一个想法!我找到了一个使用.Skip(n-1).Take(1)行N的Score字段值的解决方案(在本例中为3.Skip(n-1).Take(1)行select.Skip(n-1).Take(1),选择score值大于等于all的所有行,如下:上面是C#LearningTutorial:LINQtoEntities相当于sql“TOP(n)WITHTIES”共享的所有内容。如果对大家有用,需要进一步了解C#学习教程,希望大家多多关注——qryPeopleOrderedByScore=datamodel.People。OrderByDescending(p=>p.Score);topPeopleList=(从qryPeopleOrderedByScore中的p让lastPersonInList=qryPeopleOrderedByScore.Skip(2).Take(1).FirstOrDefault()其中lastPersonInList==null||p.Score>=lastPersonInList.Score选择p).ToList();本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如有转载请注明出处:
