2010-05-18 3 views
2

У меня есть большой набор отправляемых электронных сообщений и коды состояния.Получить только последнюю строку, сгруппированную по столбцу

ID Recipient   Date  Status 
1 [email protected] 01/01/2010  1 
2 [email protected] 02/01/2010  1 
3 [email protected] 01/01/2010  1 
4 [email protected] 02/01/2010  2 
5 [email protected] 03/01/2010  1 
6 [email protected] 01/01/2010  1 
7 [email protected] 02/01/2010  2 

В этом примере:

  • все письма, отправленные кого-то имеют статус
  • средней электронной почты (по дате) отправляется их имеет статус , но последний -
  • последнее сообщение, отправленное на других имеет статус

Что мне нужно, чтобы получить это подсчет всех писем, отправленных каждому человеку, и что код состояния последнее было.

Первая часть довольно проста:

SELECT Recipient, Count(*) EmailCount 
FROM Messages 
GROUP BY Recipient 
ORDER BY Recipient 

Который дает мне:

Recipient   EmailCount 
[email protected] 2 
[email protected] 3 
[email protected] 2 

Как я могу получить самый последний код состояния тоже?

Конечный результат должен быть:

Recipient   EmailCount LastStatus 
[email protected]   2   1 
[email protected]    3   1 
[email protected]   2   2 

Спасибо.

(Сервер Microsoft SQL Server 2008, запрос бежится через OleDbConnection в .Net)

+1

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

+0

Временная метка на самом деле имеет достаточно высокое разрешение, чтобы это не было проблемой, и даже если бы это было так, «любой SQL, возвращаемый с помощью ORDER BY», достаточно хорош. – Cylindric

ответ

4

Это пример «макс в группе» запрос , Я думаю, что это проще всего понять, разделив его на два подзапроса и затем присоединив результаты.

Первый подзапрос - это то, что у вас уже есть.

Второго подзапрос использует функцию оконной ROW_NUMBER на номер сообщения электронной почты для каждого получателя, начиная с 1 для самого последнего, а затем 2, 3, и т.д. ...

Результатов первого запроса затем соединяется с результат второго запроса, который имеет номер строки 1, то есть самый последний. Выполнение этого способа гарантирует, что вы получите только одну строку для каждого получателя в случае наличия связей.

Вот запрос:

SELECT T1.Recipient, T1.EmailCount, T2.Status FROM 
(
    SELECT Recipient, COUNT(*) AS EmailCount 
    FROM Messages 
    GROUP BY Recipient 
) T1 
JOIN 
(
    SELECT 
     Recipient, 
     Status, 
     ROW_NUMBER() OVER (PARTITION BY Recipient ORDER BY Date Desc) AS rn 
    FROM Messages 
) T2 
ON T1.Recipient = T2.Recipient AND T2.rn = 1 

Это дает следующие результаты:

Recipient   EmailCount Status 
[email protected] 2   2  
[email protected] 2   1  
[email protected]  3   1  
+0

Отлично! Большое спасибо. – Cylindric

0

Вы можете использовать функции ранжирования для этого. Нечто подобное (не проверено):

WITH MyResults AS 
(
    SELECT Recipient, Status, ROW_NUMBER() OVER(Recipient ORDER BY ( [date] DESC)) AS [row_number] 
    FROM Messages 
) 
SELECT MyResults.Recipient, MyCounts.EmailCount, MyResults.Status 
FROM (
    SELECT Recipient, Count(*) EmailCount 
    FROM Messages 
    GROUP BY Recipient 
) MyCounts 
INNER JOIN MyResults 
ON MyCounts.Recipient = MyResults.Recipient 
WHERE MyResults.[row_number] = 1 
2

Это не очень красиво, но я бы, вероятно, просто использовать пару подзапросы:

SELECT Recipient, 
    COUNT(*) EmailCount, 
    (SELECT Status 
    FROM Messages M2 
    WHERE Recipient = M.Recipient 
     AND Date = (SELECT MAX(Date) 
        FROM Messages 
        WHERE Recipient = M2.Recipient)) 
FROM Messages M 
GROUP BY Recipient 
ORDER BY Recipient 
2
SELECT 
    M.Recipient, 
    C.EmailCount, 
    M.Status 
FROM 
    (
    SELECT Recipient, Count(*) EmailCount 
    FROM Messages 
    GROUP BY Recipient 
    ) C 
    JOIN 
    (
    SELECT Recipient, MAX(Date) AS LastDate 
    FROM Messages 
    GROUP BY Recipient 
    ) MD ON C.Recipient = MD.Recipient 
    JOIN 
    Messages M ON MD.Recipient = M.Recipient AND MD.LastDate = M.Date 
ORDER BY 
    Recipient 

я нашел агрегаты в основном лучше масштабируются, то функции ранжирования

+0

+1 Мой опыт тоже. В порядке уменьшения удобочитаемости, но повышение производительности: функция ранжирования -> агрегаты -> крест применяется с CTE. – Andomar

1

Вы не можете легко это единственный запрос, потому что count (*) - это групповая функция, тогда как последнее состояние происходит от sp чистый ряд. Вот запрос, чтобы получить последний статус для каждого пользователя:

SELECT M.Recipient, M.Status FROM Messages M 
WHERE M.Date = (SELECT MAX(SUB.Date) FROM MESSAGES SUB 
    WHERE SUB.Recipient = M.Recipient) 
Смежные вопросы