2010-02-17 2 views
1

Я пытаюсь преобразовать запрос Linq в SQL. Мой запрос Linq выглядит так:Можете ли вы написать пользовательскую функцию агрегации в SQL? Можете ли вы запросить содержимое группы?

from s in Somethings 
where s.CreatedTime >= new DateTime(2010, 01, 01) 
where s.CreatedTime < new DateTime(2010, 02, 01) 
group s by s.Data into grouping 
select grouping.OrderByDescending(s => s.CreatedTime) 
       .ThenByDescending(s => s.UpdatedTime) 
       .First(); 

На словах предполагается, что все вещи будут получены с определенного месяца. Затем сгруппируйте их по определенному ключу. Для каждого ключа мне нужен последний созданный элемент. Если два элемента с одним и тем же ключом были созданы одновременно, я хочу разорвать связи по последнему обновлению.

До сих пор я это для SQL

SELECT s1.* 
FROM Somethings s1 
JOIN (
    SELECT s.Date AS Data, MAX(CreatedTime) AS CreatedTime 
    FROM Somethings s 
    WHERE s.CreatedTime >= '20100101' 
    AND s.CreatedTime < '20100201' 
    GROUP BY s.Data 
) s2 ON s1.Data = s2.Data 
    AND s1.CreatedTime = s2.CreatedTime 

Это работает, но я не могу контролировать, как связи разрываются.

То, что я действительно хочу, - это способ произвольно сортировать каждую группу, как я могу, в Linq. Я хочу определить свою собственную функцию агрегации, которая принимает набор строк и возвращает одну строку. Возможно ли это в SQL, или Linq более выразительным? Функции агрегации SQL MAX, MIN, COUNT и т. Д., Похоже, не являются функциями первого класса, такими как их эквивалент в Linq. Конечно, это может быть просто нехватка знаний SQL.

Вот составлен пример иллюстрирует то, что я хочу сделать в SQL:

SELECT (SELECT * 
     FROM grouping 
     ORDER BY CreatedTime DESC, UpdatedTime DESC 
     LIMIT 1) 
FROM Somethings s 
WHERE s.CreatedTime >= '20100101' 
    AND s.CreatedTime < '20100201' 
GROUP BY s.Data AS grouping 

В этом примере мой нелегальный внутренний запрос служит ту же роль, как функция агрегации.

ответ

2

На самом деле это не агрегация, это всего лишь групповой максимум. ROW_NUMBER это самый простой способ, чтобы написать эти запросы:

;WITH CTE AS 
(
    SELECT 
     Query, CreatedTime, UpdatedTime, <other_columns>, 
     ROW_NUMBER() OVER 
     (
      PARTITION BY Query 
      ORDER BY CreatedTime DESC, UpdatedTime DESC 
     ) AS RowNum 
    FROM Somethings 
    WHERE CreatedTime >= '20100101' 
    AND CreatedTime < '20100201' 
) 
SELECT * 
FROM CTE 
WHERE RowNum = 1 

Это не обязательно является наиболее эффективным, но это достаточно хорошо в большинстве случаев. И приятно то, что вы можете изменить это, чтобы сделать топ-2 на группу, 3-е и т. Д., И у вас есть полный контроль над связями.

(P.S. Я надеюсь, что вы на самом деле не имя столбца «Query»)

+0

К сожалению, запрос был домен конкретный материал просачивается в мой domainless вопрос. Я отредактировал его. Теперь я буду исследовать эту вещь row_number :) – dmnd

0

Использование группы В sql выравнивает записи в группе. Это позволяет выполнять агрегатные функции в группе, возвращая информацию о группе (min, max, count и т. Д.), Но отдельные записи в каждой группе недоступны.

Может быть, это не ответ на ваш вопрос ...

Добавить еще один уровень, в группу снова, с максимальным временем обновляется ... Это должно решить вашу проблему.

SELECT sFinal.* FROM Somethings sFinal 
JOIN 
    (
    SELECT s1.Query, MAX(UpdatedTime) AS UpdatedTime 
    FROM Somethings s1 
    JOIN (
      SELECT s.Query AS Query, MAX(CreatedTime) AS CreatedTime 
      FROM Somethings s 
      WHERE s.CreatedTime >= '20100101' 
      AND s.CreatedTime < '20100201' 
      GROUP BY s.Query 
     ) s2 
    ON s1.Query = s2.Query 
    AND s1.CreatedTime = s2.CreatedTime 
    GROUP BY s1.Query 
    ) s3 
ON sFinal.Query = s3.Query AND sFinal.UpdatedTime = s3.UpdatedTime 

Теперь, если у них есть соответствующее значение CreatedTime и UpdateTime, это вернет более одной записи. но это должно быть то, что вы искали.