Сводные значения из первой и последней строки - PullRequest
0 голосов
/ 23 января 2012

У меня есть таблица USERS.

У каждого пользователя есть подключения в таблице CONNECTIONS.

Каждое соединение имеет дату и время, и некоторые ссылочные свойства, такие как часовой пояс, хранятся в справочной таблице TZ.

Я бы хотел выбрать ID пользователя и TimeZoneLabel для первого и последнего соединения.Даже если у пользователя нет соединения (поэтому будет отображаться NULL или что-то еще)

Сделайте что-то вроде:

Select USERS.id,
min(TZ.label),
max(TZ.label)

from USERS
join CONNECTION on USERS.id = CONNECTIONS.userid
join TZ on TZ.id = CONNECTIONS.tzid

group by USERS.id
order by max(CONNECTIONS.dateconn)

Но я не могу добиться этого.Я нашел статьи в сети об этом, но ничего не получается, когда я пытаюсь.Приведенный выше пример не работает для метки, так как нет реальных минимальных / максимальных значений, кроме того, которое использовалось в первом СОЕДИНЕНИИ, и того, которое использовалось в последнем.

И у меня их много в моемреальный запрос, поэтому я бы хотел избежать слишком большого количества подвыборов.

Ответы [ 3 ]

1 голос
/ 23 января 2012

Без часовых поясов:

SELECT 
       u.id             AS userId
     , MIN(c.dateconn)  AS firstConnectionDatetime
     , MAX(c.dateconn)  AS lastConnectionDateTime
FROM Users AS u
  LEFT JOIN Connection AS c
    ON u.id = c.userid    
GROUP BY u.id    
ORDER BY lastConnectionDateTime

С часовыми поясами (при условии, что Connection таблица имеет id как Primary Key):

SELECT 
       u.id             AS userId
     , ConMin.dateconn  AS firstConnectionDatetime
     , ConMax.dateconn  AS lastConnectionDateTime
     , TzMin.label      AS firstTimeZoneLabel
     , TzMax.label      AS lastTimeZoneLabel
FROM Users AS u
  LEFT JOIN Connection AS ConMax
    ON ConMax.id =
        ( SELECT c.id
          FROM Connection AS c
          WHERE u.id = c.userid 
          ORDER BY c.dateconn DESC
          LIMIT 1
        )
  LEFT JOIN TzMax
    ON TzMax.id = ConMax.tzid
  LEFT JOIN Connection AS ConMin
    ON ConMin.id =
        ( SELECT c.id
          FROM Connection AS c
          WHERE u.id = c.userid 
          ORDER BY c.dateconn ASC
          LIMIT 1
        )
  LEFT JOIN TzMin
    ON TzMin.id = ConMin.tzid

Составной (userid, dateconn, id) индекс для таблицы Connection может повысить производительность.

1 голос
/ 23 января 2012

Есть немного объяснений, чтобы пойти с этим ответом - фактический запрос, который вы ищете, находится внизу.

Это пример выбора не только max / min-field-per-group, но и других соответствующих ему полей.

Канонический способ сделать это - LEFT JOIN Приготовление стола к себе.Например, чтобы выбрать всю строку, соответствующую самому последнему соединению из CONNECTIONS, вы должны сделать:

SELECT c.userid, c.tzid as latestTZ, c.dateconn as latestConn
FROM CONNECTIONS c
LEFT JOIN CONNECTIONS c2 ON c.userid=c2.userid AND c.dateconn<c2.dateconn
WHERE c2.dateconn IS NULL
ORDER BY c.userid;

Это, по существу, присоединяет CONNECTIONS к себе на userid и формирует все возможныепара дат подключения в пределах этого идентификатора пользователя, где c.dateconn<c2.dateconn.Если в c2 нет строки, которая имеет большую дату, чем c, то вы выбрали самую большую (т.е. самую последнюю) дату.JOIN гарантирует, что вы также выбираете остаток соответствующей строки из таблицы.

Имея это в виду, именно так мы бы выбрали первую дату подключения и метку для каждого пользователя (с NULL если они никогда не подключались. Если вы не хотите, чтобы это поведение (то есть показывало только пользователей, которые подключились), то вы можете полностью игнорировать таблицу USERS.

SELECT u.id,c.dateconn as firstConnection,TZ.label AS firstTZ
FROM USERS u
LEFT JOIN CONNECTIONS c ON u.id=c.userid
LEFT JOIN CONNECTIONS c2 ON c.userid=c2.userid AND c.dateconn > c2.dateconn
LEFT JOIN TZ ON c.tzid=TZ.id
WHERE c2.dateconn IS NULL;

Чтобы выбрать последнюю версиюто же самое, за исключением того, что вы поменяете > на <:

SELECT u.id,c.dateconn as latestConnection,TZ.label AS latestTZ
FROM USERS u
LEFT JOIN CONNECTIONS c ON u.id=c.userid
LEFT JOIN CONNECTIONS c2 ON c.userid=c2.userid AND c.dateconn < c2.dateconn
LEFT JOIN TZ ON c.tzid=TZ.id
WHERE c2.dateconn IS NULL;

Ваш запрос немного сложнее, так как вы хотите выбрать не только min или max, но оба мин. и макс.

Решение

Я думаю, что вы могли бы UNION предыдущие два запроса, ИЛИ вы могли бы сделать этовсе в один фол ударили, по сути, JOIN - объединяя два запроса:

# MIN & MAX
SELECT u.id, c.dateconn as firstCon, TZ.label as firstTZ, 
             c3.dateconn as latestCon, TZ2.label as latestTZ
FROM USERS u
LEFT JOIN CONNECTIONS c ON u.id=c.userid
LEFT JOIN CONNECTIONS c2 ON c.userid=c2.userid AND c.dateconn > c2.dateconn
LEFT JOIN CONNECTIONS c3 ON c.userid=c3.userid AND c3.dateconn >= c.dateconn
LEFT JOIN CONNECTIONS c4 ON c3.userid=c4.userid AND c3.dateconn < c4.dateconn
LEFT JOIN TZ ON TZ.id=c.tzid
LEFT JOIN TZ TZ2 ON TZ2.id=c3.tzid
WHERE c2.dateconn IS NULL
AND c4.dateconn IS NULL
ORDER BY u.id;

Пара (c,c2) находит первую дату / часовой пояс соединения, а пара (c3,c4) находит последний.

Кроме того, для объединения с c3 фактически не требуется квантификатор c3.dateconn>=c.dateconn (все, что ему нужно, это присоединиться к userid), но это лишний бит сужает строки, к которым мы должны присоединиться.Это связано с тем, что, поскольку мы ищем самую последнюю (т.е. МАКСИМАЛЬНУЮ) дату в таблицах (c3,c4), а c содержит дату MIN, нам нужно только смотреть на строки, для которых максимальная дата>> = минимальная дата.

0 голосов
/ 23 января 2012

Вместо JOIN попробуйте LEFT JOIN. Также перед ORDER BY добавьте GROUP BY USERS.id

...