0

We do have a database with thousand of entries. we would like to get the "ranked position" for a specific item by its name.

Since there is a lot of data we would like to avoid bring query ALL data in order to determine row index (using ToList() and IndexOf() for instance).

I tried using

        List<Ranking> ranking = _context.Testes
           .OrderByDescending(t => t.Val)
           .Select((d, i) => new Ranking() {
               Name = d.Name,
               Ranking= i
           }).First(d=>d.Name = "Test");

but I got this error:

'value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[WebApplication4.Models.Teste]).OrderByDescending(t => t.Val).Select((d, i) => new Ranking() {Name = d.Name, Ranking = i})': This overload of the method 'System.Linq.Queryable.Select' is currently not supported.

Is that possible somehow?

3
  • Paging with Skip and Take? Commented Jun 13, 2017 at 13:34
  • Skip and take will get the nth position of the list. I would like to provide a "Name" and query the ranked position of it. Commented Jun 13, 2017 at 13:39
  • 2
    You can't translate this Select() overload to SQL. ORMs aren't meant for reporting and this is 100% a reporting query. SQL Server offers ranking functions like ROW_NUMBER, RANK and DENSE_RANK. You could create a view that calculates the rank and map your rankings to it, eg CREATE VIEW Rankings AS SELECT Name,RANK OVER(ORDER BY Val) Ranking From Tests. EF will probably map the Ranking class to the Rankings view by convention. If not, you map it using the Table attribute Commented Jun 13, 2017 at 14:00

2 Answers 2

3

You can't translate this Select() overload to SQL. ORMs aren't meant for reporting and this is 100% a reporting query.

SQL Server offers ranking functions like ROW_NUMBER, RANK and DENSE_RANK. You could create a view that calculates the rank and map your rankings to it, eg :

CREATE VIEW Rankings 
AS 
SELECT 
    Name,
    DENSE_RANK() OVER(ORDER BY Val) Ranking 
From Tests

DENSE_RANK() will return the same rank number if two records tie and continue with the next rank number. ROW_NUMBER will just use incrementing numbers. If you use ROW_NUMBER you should probably use additional sorting critera to avoid generating random rankings for ties.

EF will probably map the Ranking class to the Rankings view by convention. If not, you map it using the Table attribute or ToTable if you use Code-First configuration :

[Table("Rankings")
public class Ranking
{
    public string Name{get;set;}
    public int Ranking {get;set;}
}

Retrieving a specific ranking requires only a Where() clause:

var someRanking=context.Rankings.Where(r=>r.Name=someName);
Sign up to request clarification or add additional context in comments.

Comments

1

In LINQ something like (note you must handle ties to get a well-defined ranking)

            var q = from t in db.Testes
                    where t.Name == "whatever"
                    select new
                      {
                        Testes =t,
                        Rank =1+db.Testes.Where(ot => ot.Val < t.Val || (ot.Val == t.Val && ot.Id < t.Id) ).Count()
                      };

which translates to

SELECT 
    [Project1].[Id] AS [Id], 
    [Project1].[Val] AS [Val], 
    [Project1].[Name] AS [Name], 
    1 + [Project1].[C1] AS [C1]
    FROM ( SELECT 
        [Extent1].[Id] AS [Id], 
        [Extent1].[Val] AS [Val], 
        [Extent1].[Name] AS [Name], 
        (SELECT 
            COUNT(1) AS [A1]
            FROM [dbo].[Testes] AS [Extent2]
            WHERE ([Extent2].[Val] < [Extent1].[Val]) OR (([Extent2].[Val] = [Extent1].[Val]) AND ([Extent2].[Id] < [Extent1].[Id]))) AS [C1]
        FROM [dbo].[Testes] AS [Extent1]
        WHERE N'whatever' = [Extent1].[Name]
    )  AS [Project1]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.