【问题标题】:Converting SQL Rank() to LINQ, or alternative将 SQL Rank() 转换为 LINQ 或替代方法
【发布时间】:2012-04-10 01:33:34
【问题描述】:

我有以下 SQL 语句,可以按预期/预期工作。但是我想将它翻译成 LINQ 语句(Lambda??),以便它适合我的 DAL 的其余部分。但是我看不出如何在 LINQ 中模拟 Rank()。

我在这里发布它的原因可能是错误的,是为了看看是否有人可以替代Rank() 语句,以便我可以切换它。或者,如果有一种方法可以在 LINQ 中表示 Rank(),那也将不胜感激。

USE CMO

SELECT      vp.[PersonID] AS [PersonId]
            ,ce.[EnrollmentID]
            ,vp.[FirstName]
            ,vp.[LastName]
            ,ce.[EnrollmentDate]
            ,ce.[DisenrollmentDate]
            ,wh.WorkerCategory

FROM  [dbo].[vwPersonInfo] AS vp
            INNER JOIN 
            (
                  [dbo].[tblCMOEnrollment] AS ce
                  LEFT OUTER JOIN
                        (
                              SELECT   *
                                          ,RANK()OVER(PARTITION BY EnrollmentID ORDER BY CASE WHEN EndDate IS NULL THEN 1 ELSE 2 END, EndDate DESC, StartDate DESC) AS whrank 
                              FROM  [dbo].[tblWorkerHistory]
                              WHERE WorkerCategory = 2
                        ) AS wh 
                              ON ce.[EnrollmentID] = wh.[EnrollmentID] AND wh.whrank = 1
            ) 
                  ON vp.[PersonID] = ce.[ClientID]

WHERE (vp.LastName NOT IN ('Client','Orientation','Real','Training','Matrix','Second','Not'))
AND (
            (wh.[EndDate] <= GETDATE())
            OR wh.WorkerCategory IS NULL
      ) 
AND (
            (ce.[DisenrollmentDate] IS NULL) 
            OR (ce.[DisenrollmentDate] >= GetDate())
      )

【问题讨论】:

  • 我在想,也许我应该重新设计它,以简单地询问如何使用 Rank() 分解子选择。这会是一个更好的问题吗?
  • 您可以使用带有 rank 子句的视图 - 等待其他人知道是否可以使用原始 Linq。
  • 您使用的是什么 ORM:LINQ to SQL、实体框架...?
  • @KirillPolishchuk:LINQ to SQL。

标签: c# sql linq linq-to-sql


【解决方案1】:

这是一个示例,展示了我将如何在 Linq 中模拟 Rank():

var items = new[]
{
    new { Name = "1", Value = 2 },
    new { Name = "2", Value = 2 },
    new { Name = "3", Value = 1 },
    new { Name = "4", Value = 1 },
    new { Name = "5", Value = 3 },
    new { Name = "6", Value = 3 },
    new { Name = "7", Value = 4 },
};
  
var q = from s in items
    orderby s.Value descending
    select new 
    { 
        Name = s.Name, 
        Value = s.Value,
        Rank = (from o in items
                where o.Value > s.Value
                select o).Count() + 1 
    };

foreach(var item in q)
{
    Console.WriteLine($"Name: {item.Name} Value: {item.Value} Rank: {item.Rank}");
}

输出

Name: 7 Value: 4 Rank: 1
Name: 5 Value: 3 Rank: 2
Name: 6 Value: 3 Rank: 2
Name: 1 Value: 2 Rank: 4
Name: 2 Value: 2 Rank: 4
Name: 3 Value: 1 Rank: 6
Name: 4 Value: 1 Rank: 6

【讨论】:

  • 对我来说,这感觉就像 E=mc^2 .. 一个相对复杂问题的非常简单的解决方案
  • 就我的测试而言,似乎我正在为外部查询中的每个结果创建一个单独的查询。即使是几千个结果,这也会成为问题,因此这种方法似乎是有限的。
  • 我喜欢你对这个解决方案的想法!我从没想过这样看。做得好!我最终使用了 lamda 实现,但基于相同的概念: varrankData = data.Select(s => new{ Ranking = data.Count(x => x.Value > s.Value)+1, Name = s.Key , 分数 = s.Value});
  • 与 Ef Core 2.1 一起使用时,它会生成单个 SQL 查询
【解决方案2】:

LINQ 内置了排名功能,但在查询语法中没有。使用方法语法时,大多数 linq 函数有两个版本 - 普通版本和提供排名的版本。

一个简单的例子,只选择所有其他学生,然后将结果序列中的索引添加到结果中:

var q = class.student.OrderBy(s => s.studentId).Where((s, i) => i % 2 == 0)
.Select((s,i) => new
{
  Name = s.Name,
  Rank = i
}

【讨论】:

  • 这不是严格针对物化列表,还是真的生成对应排​​名的SQL?
  • 当我尝试使用它时,我不断收到消息“LINQ 无法识别该方法...”,直到我删除 where 子句。好像不喜欢括号里的2个参数。
  • 这应该是公认的答案,公认的表现很糟糕!
  • 请注意,这不会处理平局。如果两个项目具有相同的度量,它们将被赋予不同的等级。
【解决方案3】:

如果你想模拟排名,那么你可以使用以下 linq 查询。

      var q = (from s in class.student
                select new
                {
                    Name = s.Name,
                    Rank = (from o in class.student
                            where o.Mark > s.Mark && o.studentId == s.studentId
                            select o.Mark).Distinct().Count() + 1
                }).ToList();

您可以按如下方式使用订单:

      var q = (from s in class.student
                orderby s.studentId 
                select new
                {
                    Name = s.Name,
                    Rank = (from o in class.student
                            where o.Mark > s.Mark && o.studentId == s.studentId
                            select o.Mark).Distinct().Count() + 1
                }).ToList();

但是在这个查询中 order by 并不重要。

【讨论】:

  • @Refracted Paladin 如果您的问题得到解决,则标记​​为答案。
  • 为什么是.Distinct()RANK() 函数的定义要求您忽略它。您的答案与DENSE_RANK()的输出相匹配
  • 感谢DENSE_RANK() 的实施,尽管它不是问题的一部分。
【解决方案4】:

基于@Totero 的回答,但使用 lamda 实现。 更高的分数 = 更高的排名。

var rankedData = data.Select(s => new{
                    Ranking = data.Count(x => x.Value > s.Value)+1,
                    Name = s.Key,
                    Score = s.Value});

对于这个输入:

{ 100, 100, 98, 97, 97, 97, 91, 50 }

你会得到这个输出:

  • 得分:排名
  • 100 : 1
  • 100 : 1
  • 98 : 3
  • 97:4
  • 97:4
  • 97:4
  • 91:6
  • 50 : 7

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-23
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多