【问题标题】:Select many fields applying DISTINCT to only one particular field选择多个字段,仅将 DISTINCT 应​​用于一个特定字段
【发布时间】:2023-03-20 05:46:01
【问题描述】:

在 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

非常感谢。

【问题讨论】:

  • 简短的回答是 DISTINCT 无法做到这一点。从您的结果集中,您似乎正在返回 MAX(insertDate)

标签: sql sql-server


【解决方案1】:

内部查询应该为用户 'john' 的每个操作选择最大 id,外部查询将选择与内部查询中的 id 集合匹配的那些记录,因此您应该只获取每个操作的最后一个指定的用户。

select id, user, action, insertDate
from userActions
where id in (select max(id)
                 from userActions
                 where user ='john'
                 group by action)

【讨论】:

  • ? MAX 是一个聚合函数。
  • 结果集符合规范。 OP 问题是如何使用 DISTINCT 获取结果集。简短的回答是不要使用 DISTINCT。
【解决方案2】:

一个值得考虑的替代方案(在 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

(看,妈,没有聚合函数!-)

【讨论】:

  • 如果我尝试这种方式,我会得到:“窗口函数只能出现在 SELECT 或 ORDER BY 子句中。”
  • 我认为这是 SS 2005 中的问题之一(我的“不确定”)——SS'05 的简单解决方法(只是一个嵌套的 SELECT)位于 weblogs.sqlteam.com/jeffs/archive/2007/03/28/60146.aspx(但是我认为 SS 2008 仍然不需要它)。
  • 我第二个@SantiagoCorredoira。我得到同样的错误,我在 sql server 2008 上。这段代码不起作用。
【解决方案3】:

忽略 OP 不需要聚合函数(仍然不确定为什么......)

给定答案的问题是:

  1. 允许任何其他用户不是动态的 - 比如说“标记”
  2. 它假定一个动作的 max(id) 将匹配最新的动作 - 测试数据表明这一点,但我不认为这是一个规则。

因此,考虑到这些,需要构建更动态的查询

向测试数据添加了另外 2 行

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

答案没有给出 OP 想要的内容

这是我的初稿 - 稍后会整理

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 

更新....

第二个版本使用 ID 获取不同的行,其中特定用户可能多次执行操作,即如果测试数据也有以下行

 9, john, delete, 06/01/09

那么您需要在第 6 行和第 9 行之间决定要返回哪一个。我随意选择使用max(id),因为我猜数据很重要而不是行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

【讨论】:

    【解决方案4】:

    不知道如何仅使用 SQL 来完成。您可以执行完整查询(看起来您想按 InsertDate DESC 排序),然后仅手动提取您想要的查询。

    set s = new set()
    while (has more results) {
       var r = next result
       if (!s.contains(r)) {
          process result
          s.add(r)
       }
    }
    

    【讨论】:

    • 这将比纯 sql 答案运行得慢,并且可能返回许多额外的、不必要的行
    【解决方案5】:

    如果您有一组固定的操作,您可以为每个操作编写一个查询,然后将结果合并在一起:

    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
    

    【讨论】:

    • TOP 1 with ORDER BY DESC 实际上是一个聚合函数。
    猜你喜欢
    • 1970-01-01
    • 2015-05-06
    • 1970-01-01
    • 1970-01-01
    • 2013-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多