Выберите много полей, применяя DISTINCT только к одному конкретному полю - PullRequest
1 голос
/ 01 июня 2009

Как в SQL Server можно выбрать много полей (без функции агрегирования) и применить инструкцию DISTINCT только к одному конкретному полю?

Например: если у меня есть таблица, в которой я храню действия пользователя, псевдосхема будет выглядеть так:

UserActions
------------
id,
User,
Action
insertDate

Я хочу получить последние действия для данного пользователя без повторения поля «Действие»?

Например, если содержимое таблицы:

1, john, update, 01/01/09
2, john, update, 01/02/09
3, john, update, 01/03/09
4, john, delete, 01/04/09
5, john, insert, 01/05/09
6, john, delete, 01/06/09

Я хотел бы получить:

6, john, delete, 01/06/09
5, john, insert, 01/05/09
3, john, update, 01/03/09

Большое спасибо заранее.

Ответы [ 5 ]

5 голосов
/ 01 июня 2009

Внутренний запрос должен выбрать максимальный идентификатор для каждого действия для пользователя «Джон», внешний запрос выберет те записи, которые соответствуют коллекции идентификаторов во внутреннем запросе, поэтому вы должны получить только последнее из каждого действия для указанный пользователь.

select id, user, action, insertDate
from userActions
where id in (select max(id)
                 from userActions
                 where user ='john'
                 group by action)
1 голос
/ 01 июня 2009

Игнорирование ОП не требует агрегатных функций (все еще не знаю, почему ...)

Вопрос, который я имею с данным ответом:

  1. Это не динамично, чтобы разрешить для любого другого пользователя - скажем, «Марк»
  2. предполагается, что max (id) для действия будет соответствовать последнему действию - тестовые данные предполагают это, но я бы не принял это как правило.

так что с учетом этого необходимо построить более динамический запрос

с добавлением еще 2 строк к тестовым данным

 7, john, update, 04/01/09
 8, mark, insert, 01/02/09

ответ не дает того, чего хотел ОП

Вот мой первый черновик быстро - позже приберусь

select
    userActions.id,
    userActions.[user],
    userActions.Action,
    userActions.insertDate

from
userActions
join
    (
    select
        [user], action, max(insertdate) as maxinsertdate
    from userActions
    group by
        [user], action
    ) aggsubquery
    on userActions.[user] = aggsubquery.[user] 
         and userActions.action = aggsubquery.action 
         and userActions.insertdate = aggsubquery.maxinsertdate 

Обновление ....

2-я версия использует идентификатор, чтобы получить отдельную строку, в которой может быть несколько событий определенного пользователя, т. Е. Если у тестовых данных также была следующая строка

 9, john, delete, 06/01/09

тогда вам нужно будет выбрать между идентификатором строки 6 и идентификатором строки 9, какой из них вернуть. Я произвольно решил использовать max (id), так как я думаю, что важны данные, а не идентификатор строки

select
    max(userActions.id) as id,
    userActions.[user],
    userActions.Action,
    userActions.insertDate  
from
userActions
join
    (
    select
        [user], action, max(insertdate) as maxinsertdate
    from userActions
    group by
        [user], action
    ) aggsubquery
    on userActions.[user] = aggsubquery.[user] 
        and userActions.action = aggsubquery.action 
        and userActions.insertdate = aggsubquery.maxinsertdate 
group by
    userActions.[user],
    userActions.Action,
    userActions.insertDate
1 голос
/ 01 июня 2009

Одна альтернатива, которую стоит рассмотреть (в SQL Server 2008; не уверен насчет SS 2005):

SELECT id, User, Action, InsertDate
FROM Table
WHERE User = 'john'
AND ROW_NUMBER() 
    OVER(PARTITION BY Action ORDER BY InsertDate DESC) 
    = 1

(смотри, мама, без агрегатных функций! -)

0 голосов
/ 01 июня 2009

Если у вас есть фиксированный набор действий, вы можете написать запрос для TOP 1 каждого и объединить результаты вместе:

SELECT TOP 1 [id], [User], [InsertDate] 
FROM [UserActions] 
WHERE [Action] = 'insert' 
ORDER BY [InsertDate] DESC

UNION

SELECT TOP 1 [id], [User], [InsertDate] 
FROM [UserActions] 
WHERE [Action] = 'update' 
ORDER BY [InsertDate] DESC

UNION

SELECT TOP 1 [id], [User], [InsertDate] 
FROM [UserActions] 
WHERE [Action] = 'delete' 
ORDER BY [InsertDate] DESC
0 голосов
/ 01 июня 2009

Не уверен, как это сделать, используя только SQL. Вы можете сделать полный запрос (похоже, что вы хотите отсортировать по InsertDate DESC), а затем вытащить только те, которые вы хотите вручную.

set s = new set()
while (has more results) {
   var r = next result
   if (!s.contains(r)) {
      process result
      s.add(r)
   }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...