【问题标题】:Need some help with a simple SQL query需要一些关于简单 SQL 查询的帮助
【发布时间】:2009-02-13 22:28:26
【问题描述】:

我有一个将状态信息转储到数据库的游戏。例如。已连接、丢失连接、抓取屏幕转储等。

每个状态都由一个名为 StateTypeId 的字段定义,它是简单查找表 'StateTypes' 的外键。

现在,我正在尝试查找所有当前为“Connected”的人(例如 StateTypeId == 1),但我不知道该怎么做。当我说当前连接时,我的意思是用户没有'LostConnection'状态(即StateType == 2)作为他们最近的条目。

我是这么想的。

SELECT UserId
FROM UserData
WHERE StateType = 1
GROUP BY UserId

或

SELECT UserId
FROM UserData
WHERE StateType != 1
GROUP BY UserId

但这会返回所有结果,而不仅仅是最近的结果。

有什么建议吗?

(我猜我错过了TOP(1) 和一些排名/顺序?)

编辑

DOH!我忘了提,我在表中有一个 DateTime 列 -> DateEntered。

编辑 2 - 澄清问题和一些示例数据。

我已经尝试了建议的解决方案,但运气不佳。因此,我将提供一些示例数据,看看这是否有助于澄清我的问题。

ID State          UserName DateAdded
1  Connected      Foo      1/1/2000 12:00 
2  Connected      Bar      1/1/2000 12:01  
3  ScreenShot     Foo      1/1/2000 12:05 
4  LostConnection Foo      1/1/2000 12:06
5  Connected      Joe      1/1/2000 12:10
6  LostConnection Joe      1/1/2000 12:15
7  Screenshot     Bar      1/1/2000 12:16
8  Connected      Jane     1/1/2000 12:22

所以看看这个,(当前)连接的人是 Bar 和 Jane。 Foo 和 Joe 走了。另请注意,Bar 的最后一个活动是截图,意思是,他的最后一个活动不是 LostConnection 状态。

HTH。

【问题讨论】:

  • UserData 中还有哪些其他列?
  • 糟糕。我忘了提到 DateTime 列 :) 已编辑初始帖子。
  • 也许这很明显,但是如果您经常执行此查询并且您有很多状态更改,那么您最好将最后一个状态非规范化到 UserData 表中。 Pro:更容易/更快地查询。缺点:更新数据库更难/更慢(必须做两件事并始终正确)
  • 听起来很适合用作触发器...

标签: sql tsql


【解决方案1】:

您要查找的是 StateType 为 1 或 2 的“最新”条目:

然后您要为每个用户确定“最后一个”:

select UserName, max(DateAdded) as LastDate
from UserData
where State IN ('Connected', 'LostConnection')
Group by UserName

然后你需要检查这些的状态(这需要与原始表连接):

SELECT UserName
FROM UserData allData join 
    (select UserName, max(DateAdded) as LastDate
    from UserData
    where State IN ('Connected', 'LostConnection')
    Group by UserName) as lastData on allData.UserName = lastData.UserName
        and allData.DateAdded = lastData.LastDate
WHERE StateType = 'Connected'

另一种加入方式是使用 UserData 的 id,如下所示:

SELECT UserName
FROM UserData allData join 
    (select max(id) as id -- if ids are assigned in ascending order
    from UserData
    where State IN ('Connected', 'LostConnection')
    Group by UserName) as lastData on allData.id = lastData.id
WHERE StateType = 'Connected'

【讨论】:

  • 索引对于从该查询中获得良好性能非常重要。
  • 我想你的意思是 GROUP BY UserID
  • 嗯。我仍然没有运气:(你可以查看更新的初始帖子以提供帮助吗?
  • 更改了列名以反映您的示例数据。如果您仍然遇到问题,您能指出问题所在吗? (错误或太多或错误的数据?)
  • 我认为我得到了太多错误的数据。我正在考虑使用 TRIGGER 建议。
【解决方案2】:

如果您有日期时间条目或时间戳,您可以这样做:

Select UserID
From UserData
Where StateType = 1
Group by UserID
Having Date = Max(Date)

【讨论】:

    【解决方案3】:

    我认为值得测试 NOT EXISTS 方法的性能。

    即查找具有适当 StateType 且没有更新记录的用户。

    SELECT UD1.UserId
    FROM  UserData AS UD1
    WHERE UD1.StateType = 1
          AND NOT EXISTS
          (
    --        Check for newer records:
              SELECT *
              FROM UserData AS UD2
              WHERE UD2.DateEntered > UD1.DateEntered
          )
    

    如果它们有很多行存储在 UserData 中,并且/或者您将它们保存很长时间,那么这种方法可能会表现不佳。

    如果是这种情况,并且这是一个经常发生的查询,我将创建一个 UserCurrentState 表(列 = UserId,StateType),当 StateType 更改时更新它(我会在 UserData 表上放置一个触发器来强制执行此操作)

    UserData 中的StateType 索引不够好,无法使用。您可以拥有一个包含 DateEntered、StateType、UserId 的索引,该索引将覆盖查询,因此应该使用,但实际上只有在您可以限制检查的 DateEntered 的范围时才会有帮助。如果您只是在寻找最后一小时/一天的罚款,如果您正在寻找自开始以来的时间,那么它不会有太大帮助。

    【讨论】:

      【解决方案4】:

      其实这样可能更高效(SQL Server 2005 以上)

      SELECT UserId
      FROM
      (
          SELECT  *, row_number() 
              OVER
              (
                  partition BY UserId 
                  ORDER BY DateEntered DESC
              ) AS RecordNumber
          FROM UserData
      ) AS UD
      WHERE      RecordNumber = 1
              AND StateType = 1
      

      【讨论】:

        【解决方案5】:

        这是另一种可能的解决方案,尽管使用 NOT EXISTS 的解决方案可能性能更好。

        SELECT U1.*
        FROM UserData U1
         LEFT OUTER JOIN UserData U2
         ON (U1.UserName = U2.UserName
          AND U1.DateAdded < U2.DateAdded
          AND U2.StateType IN (1,2))
        WHERE U1.StateType = 1
          AND U2.UserName IS NULL
        ORDER BY U1.DateAdded DESC;
        

        【讨论】:

        • 嗯。不。我试过这个,只有一排。也许我刚刚添加到初始帖子中的示例数据可能有助于澄清事情。为混乱道歉。
        • 好的,我已经更改了查询,并使用您的示例数据和几个额外的案例对其进行了测试。
        【解决方案6】:

        您将需要一个时间戳列,并且您需要编写一个查询,像这样将表连接到自身

        Select *
        From Userdata U
        Where 1 = (Select Top 1 StateType
                   From Userdata U2
                   Where U.UserId = U2.UserId
                   order by u2.TimeStamp desc)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-10-15
          • 2011-03-26
          • 2019-06-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多