【问题标题】:SQL - how to efficiently select distinct recordsSQL - 如何有效地选择不同的记录
【发布时间】:2011-10-02 13:48:38
【问题描述】:

我有一个对性能非常敏感的 SQL Server 数据库。我需要对以下问题做出有效的选择:

我有一个包含 4 个字段的简单表格:

ID [int, PK]
UserID [int, FK]
Active [bit]
GroupID [int, FK]

每个 UserID 可以在 Active='false' 的 GroupID 中出现多次(以及在多个 groupID 中),但在 Active='true' 时只能出现一次。

如:

(id,userid,active,groupid)
1,2,false,10
2,2,false,10
3,2,false,10
4,2,true,10

我需要从某个组中的表中选择所有不同的用户,它应该保存用户的最后一个活动状态。如果用户处于活动状态 - 它不应该返回用户的非活动状态,如果它在某个时间点是这样的。

天真的解决方案是双重选择 - 一个选择所有活动用户,然后一个选择所有未出现在第一个选择语句中的非活动用户(因为每个用户可能在某些时候处于非活动状态时间点)。但这会运行第一个选择(与活动用户一起)两次 - 这是非常不需要的。

有没有什么聪明的方法可以只选择一次来获得所需的查询?想法?

提前非常感谢!

【问题讨论】:

  • "last" 和 "point in time" 暗示了一些日期字段,但我没有看到。
  • 还是id单调递增?
  • 没错! ID 是自动递增的。
  • 您展示了一个非规范化的数据集,其中 groupid 似乎依赖于 userid。这是您数据的准确表示,还是 groupid 可以在单个用户 ID 的记录中有所不同?
  • @Larry - GroupID 不依赖于 UserID。那些是独立的。每次使用都可能出现在多个组中,并且每个组可以包含同一用户的多个记录(以及与问题无关的其他字段)。数据已标准化。

标签: sql sql-server performance sqlperformance


【解决方案1】:

这样的视图怎么样:

createview ACTIVE as select * from USERS where Active = TRUE

然后从该视图中选择一个就足够了:

select user from ACTIVE where ID ....

【讨论】:

  • 这不太好 - 因为该表中有数十万条记录 - 我不想获取所有活动记录...
  • 确实,CREATE VIEW 需要一些时间才能完成,但只运行一次!
  • 除非您正在创建物化视图,否则该视图不会在您从中读取数据之前运行,然后在您每次从中读取数据时运行。跨度>
  • 确实,感谢精度。
【解决方案2】:

试试这个:

Select
   ug.GroupId,
   ug.UserId,
   max(ug.Active) LastState
from
   UserGroup ug
group by
   ug.GroupId,
   ug.UserId

如果用户/组组合的活动字段设置为 1,您将获得 1,否则您将获得最后状态的 0。

【讨论】:

  • 这是一个非常有趣的想法.... :) 虽然看起来 MAX 函数不能用于位域
  • 但是,如果true 记录不是“最新”记录,即使用户在该组中不再活动,您仍然会得到true
【解决方案3】:

我不喜欢按照您的方式使用“isActive”列。这需要两个UPDATEs 来更改活动状态,并具有在不同记录中多次存储活动状态信息的效果。

相反,我会删除 active 字段并执行以下两件事之一:

  1. 如果您已经有一个表,其中(userid, groupid) 是(或可能是)PRIMARY KEYUNIQUE INDEX,则将active 列添加到该表。当用户对特定组变得活跃或不活跃时,仅使用 truefalse 更新该单条记录。

  2. 如果这样的表尚不存在,则使用 '(userid, groupid)as thePRIMARY KEYand the fieldactive` 创建一个表,然后按上述方式处理该表。

在任何一种情况下,您只需要查询此表(无需聚合)即可确定用户在特定组中的状态。同样重要的是,您只需存储一次truefalse 值,并且只需要UPDATE 一个值即可更改状态。最后,此表充当您可以存储特定于该用户在该组中的成员身份的其他信息的地方,这些信息仅适用于每个成员身份一次,而不是每个状态更改一次。

【讨论】:

    【解决方案4】:

    试试这个:

    SELECT t.* FROM tbl t 
    INNER JOIN (
        SELECT MAX(id) id
        FROM tbl
        GROUP BY userid 
    ) m
    ON t.id = m.id 
    

    【讨论】:

      【解决方案5】:

      不确定我是否理解您希望查询返回的内容,但无论如何。此查询将为您提供在最后一个条目中处于活动状态的组中的用户。它使用row_number(),因此您至少需要 SQL Server 2005。

      表定义:

      create table YourTable
      (
        ID int identity primary key,
        UserID int,
        Active bit,
        GroupID int
      )
      

      支持查询的索引:

      create index IX_YourTable_GroupID on YourTable(GroupID) include(UserID, Active)
      

      样本数据:

      insert into YourTable values
      (1, 0, 10),
      (1, 0, 10),
      (1, 0, 10),
      (1, 1, 10),
      (2, 0, 10),
      (2, 1, 10),
      (2, 0, 10),
      (3, 1, 10)
      

      查询:

      declare @GroupID int = 10
      
      ;with C as 
      (
        select UserID,
               Active,
               row_number() over(partition by UserID order by ID desc) as rn
        from YourTable as T
        where T.GroupID = @GroupID
      )
      select UserID
      from C
      where rn = 1 and
            Active = 1
      

      结果:

      UserID
      -----------
      1
      3
      

      【讨论】:

        猜你喜欢
        • 2018-10-12
        • 2015-12-27
        • 2011-02-12
        • 2016-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多