2013-03-08 5 views
2

Это возможный дубликат других вопросов с разделением по + рангу, но я нашел большинство этих вопросов/ответов слишком специфичными для их конкретной бизнес-логики. То, что я смотрю на это более общий вариант LINQ следующего типа запроса:LINQ (to Oracle) - Row_Number() Over Partition By

SELECT id, 
     field1, 
     field2, 
     ROW_NUMBER() OVER (PARTITION BY id 
          ORDER BY field1 desc) ROWNUM 
FROM someTable; 

Очень часто, что мы делаем с этим, чтобы обернуть его, как в чем-то вроде этого:

SELECT id, 
     field1, 
     field2 
FROM (SELECT id, 
     field1, 
     field2, 
     ROW_NUMBER() OVER (PARTITION BY id 
         ORDER BY field1 desc) ROWNUM 
     FROM someTable) 
WHERE ROWNUM = 1; 

Что возвращает строку, содержащую наибольшее значение в поле1 для каждого идентификатора. Изменение порядка с помощью курса, конечно, вернет наименьшее значение или изменит ранг на 2, получит второе наивысшее/низкое значение и т. Д. И т. Д. Есть ли способ написать запрос LINQ, который может быть выполнен на стороне сервера, что дает нам такая же функциональность? В идеале, тот, который, как и выше.

Edit: Я пробовал множество различных решений после соскабливать в Интернете, и все они в конечном итоге дает мне ту же проблему, что ответ Рида ниже делает, поскольку SQL генерируется включает APPLY.

Пару примеров я попробовал:

from p in db.someTable 
group p by p.id into g 
let mostRecent = g.OrderByDescending(o => o.field1).FirstOrDefault() 
select new { 
    g.Key, 
    mostRecent 
}; 

db.someTable 
    .GroupBy(g => g.id, (a, b) => b.OrderByDescending(o => o.field1).Take(1)) 
    .SelectMany(m => m); 

Оба эти результата очень похожи, если не идентичны, код SQL, которая использует OUTER APPLY, что Oracle не поддерживает.

+0

Для меня это кажется простой GROUP BY 'id' и' MAX (field1) '. Я что-то пропустил? –

+0

@FrancisP В текущей форме, да. Но что, если я не хочу MAX и хотел бы второго или третьего вместо этого? Я снял акцент на этой конкретной части. – Kittoes0124

+0

@ Kittoes Тот же метод должен работать, добавляя вызов «Пропустить», а затем отфильтровывая любые пустые результаты ... –

ответ

5

Вы должны быть в состоянии сделать что-то вроде:

var results = someTable 
       .GroupBy(row => row.id) 
       .Select(group => group.OrderByDescending(r => r.id).First()); 

Если вы хотите, третье самое высокое значение, вы могли бы сделать что-то вроде:

var results = someTable 
       .GroupBy(row => row.id) 
       .Select(group => group.OrderByDescending(r => r.id).Skip(2).FirstOrDefault()) 
       .Where(r => r != null); // Remove the groups that don't have 3 items 
+0

Теперь я ненавижу быть болью в заднице, но мне просто интересно: что, если бы я хотел третий или последний элемент, если нет третьего? – Kittoes0124

+0

@Kittoes Вы можете использовать '.Take (3) .Last()', чтобы получить «до третьего» элемента ... –

+2

Протестировано ваше решение, оно работает в SQL Server, но в Oracle запрос, который сгенерирован, не сработает, потому что Oracle не поддерживает «APPLY». Есть идеи? – Kittoes0124

1

альтернативный способ, с помощью подзапроса, который отдельно получает максимум field1 за каждые ID.

SELECT a.* 
FROM someTable a 
     INNER JOIN 
     (
      SELECT id, max(field1) max_field 
      FROM sometable 
      GROUP BY id 
     ) b  ON a.id = b.ID AND 
        a.field1 = b.max_field 

при преобразовании в LINQ:

from a in someTable 
join b in 
    (
     from o in someTable 
     group o by new {o.ID} into g 
     select new 
     { 
      g.Key.ID, 
      max_field = g.Max(p => p.field1) 
     } 
    ) on new {a.ID, a.field1} equals new {b.ID, field1 = b.max_field} 
select a 
Смежные вопросы