Нужна помощь с простым запросом SQL - PullRequest
0 голосов
/ 14 февраля 2009

У меня есть игра, которая выдает информацию о состоянии в базу данных. Например. Подключено, потеряно соединение, захвачен экранный дамп и т. Д.

Каждое состояние определяется полем с именем 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) и какой-то ранг / порядок вещей?)

EDIT

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

Итак, глядя на это, люди, которые связаны (в настоящее время), это Бар и Джейн. Фу и Джо ушли. Также обратите внимание, что последнее действие Бар - снимок экрана, то есть его последнее действие не было состоянием LostConnection.

НТН.

Ответы [ 6 ]

3 голосов
/ 14 февраля 2009

То, что вы ищете, это «самая последняя» запись, имеющая 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, как это:

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 голос
/ 14 февраля 2009

На самом деле, это может быть более эффективным (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
1 голос
/ 14 февраля 2009

Думаю, стоило бы протестировать производительность подхода 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 (я бы добавил Trigger в таблицу UserData, чтобы принудительно выполнить это )

StateType в UserData недостаточно хорошо индексируется, чтобы быть полезным. У вас может быть индекс с DateEntered, StateType, UserId, который будет покрывать запрос и, следовательно, должен использоваться, но на практике это будет полезно, только если вы сможете ограничить диапазон проверяемого DateEntered. Если вы ищете только последний час / день штрафа, если вы ищете с начала времени, то это не сильно поможет.

1 голос
/ 14 февраля 2009

если у вас есть дата или время, вы можете сделать это следующим образом:

Select UserID
From UserData
Where StateType = 1
Group by UserID
Having Date = Max(Date)
0 голосов
/ 15 февраля 2009

Вот еще одно возможное решение, хотя решения, использующие 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;
0 голосов
/ 14 февраля 2009

Вам понадобится столбец отметки времени, и вам нужно будет написать запрос, который присоединит таблицу к себе, например,

Select *
From Userdata U
Where 1 = (Select Top 1 StateType
           From Userdata U2
           Where U.UserId = U2.UserId
           order by u2.TimeStamp desc)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...