我有一个将状态信息转储到数据库的游戏。例如。连接,丢失连接,抓取屏幕转储等
每个状态都由一个名为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。
我尝试了建议的解决方案,但我没有太多运气。因此,我将提供一些示例数据,看看这是否有助于澄清我的问题。
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。
答案 0 :(得分:3)
您正在寻找的是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'
答案 1 :(得分:1)
如果你有日期时间条目或时间戳,你可以这样做:
Select UserID
From UserData
Where StateType = 1
Group by UserID
Having Date = Max(Date)
答案 2 :(得分:1)
我认为值得测试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表(columns = UserId,StateType),当StateType更改时会更新(我会在UserData表上放置一个Trigger来强制执行此操作) )
UserData中的StateType不能很好地编入索引以使其有用。您可以使用DateEntered,StateType,UserId来覆盖查询,因此应该使用该索引,但实际上,如果您可以限制检查的DateEntered范围,那么这只会有所帮助。如果您只是在寻找最后一小时/天的罚款,如果您从一开始就在寻找,那么它将无济于事。
答案 3 :(得分:1)
实际上,这可能更有效(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
答案 4 :(得分:0)
这是另一种可能的解决方案,尽管使用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;
答案 5 :(得分:-1)
您需要一个时间戳列,您需要编写一个将表连接到自身的查询
Select *
From Userdata U
Where 1 = (Select Top 1 StateType
From Userdata U2
Where U.UserId = U2.UserId
order by u2.TimeStamp desc)