выборка из двух таблиц и агрегатные функции

Sokil.Dmytro

Новичок
выборка из двух таблиц и агрегатные функции

есть таблица с пользователями

CREATE TABLE USRLGN(
ID Integer NOT NULL, # ID пользователя
UGRP Integer NOT NULL, # группа, к которой принадлежит пользователь
LGN Char(10) NOT NULL,
...
);

есть таблица с данными о сессиях пользователя

CREATE TABLE SESINF(
U_ID Integer NOT NULL, # ссылается на USRLGN.ID
LGN_DT Timestamp NOT NULL, # время последнего входа
LST_DT Timestamp NOT NULL, # время последнего действия пользователя
...
);

При каждом логине сюда записываются (INSERT) данные сессии и время логина, при каждом запросе страници - обновляется поле LST_DT.
Тоесть отношение один USRLGN ко многим SESINF.

Задача: выбрать всех пользователей конкретной группы USRLGN.UGRP из таблици USRLGN и добавить туда дату последнего входа и последнего действия для конкретного пользователя

Задачу решил двумя способами:

Способ 1 . SELECT usrlgn.LGN, usrlgn.UGRP, sesinf.U_ID, MAX(sesinf.LGN_DT), MAX(sesinf.LST_DT)
FROM SESINF sesinf LEFT OUTER JOIN USRLGN usrlgn ON usrlgn.ID = sesinf.U_ID
GROUP BY sesinf.U_ID, usrlgn.LGN, usrlgn.UGRP
HAVING usrlgn.UGRP = 2

Результат: Полный звиздец, так как все поля надо перечислять в списке группировки

Способ 2. первый запрос сливает данные с USRLGN по всем юзерам для USRLGN.UGRP = 2
второй запрос:
SELECT sesinf.U_ID, MAX(sesinf.LST_DT) as LST_DT, MAX(sesinf.LGN_DT) as LGN_DT
FROM SESINF sesinf LEFT OUTER JOIN USRLGN usrlgn ON usrlgn.ID = sesinf.U_ID
WHERE usrlgn.UGRP = 2
GROUP BY sesinf.U_ID
получает максимальные значения для каждого пользователя из заданной группы usrlgn.UGRP. потом два массива сливаются уже на уровне PHP. В итоге выборка юзеров по группе происходит два раза.

Как вариант решения, можно получить все айдишники пользователей в первом запросе, и избавиться от JOIN'ов во втором, сделав поиск по айдишникам WHERE sesinf.U_ID IN(id,id,id,id)

Результат: Звиздец круче первого

Ктото подскажет более адекватное решение?

-~{}~ 27.09.09 02:26:

придумал еще одну двухєтажную хренотень:

SELECT
usrlgn.*, sesinf.LGN_DT, sesinf.LST_DT
FROM
USRLGN usrlgn
LEFT OUTER JOIN (
SELECT
U_ID, MAX(LGN_DT) as LGN_DT, MAX(LST_DT) as LST_DT
FROM SESINF
GROUP BY U_ID
) as sesinf
ON usrlgn.ID = sesinf.U_ID
WHERE usrlgn.UGRP = 2

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

whirlwind

TDD infected, paranoid
Что-то я как-то смутно понял о чем речь. Может SELECT DISTINCT ?
 

Sokil.Dmytro

Новичок
выбрать надо всех пользователей которые принадлежат к указанной группе, при чем получить также и последнюю дату входа и операции. distinct тут, как по мне, ни при чем
 

whirlwind

TDD infected, paranoid
Если в SESINF много по одному юзеру, то наверное без подзапроса никак. Но группировка тут не нужна.

-~{}~ 27.09.09 03:43:

PS. кстати, что за база? в постгресе можно колонки для дистинкта указывать. в мускуле нет.
 

Sokil.Dmytro

Новичок
без групировки не получишь максимальніх дат по каждому пользователю

-~{}~ 27.09.09 03:47:

база файрберд/интербейс - дистинкт делается на все
 

nirex

Новичок
попробуй вот так, хотя вроде как та же тема
SELECT
usrlgn.*, max(sesinf.LGN_DT), max(sesinf.LST_DT)
FROM
USRLGN usrlgn
LEFT JOIN SESINF ON usrlgn.ID = sesinf.U_ID
WHERE usrlgn.UGRP = 2
GROUP BY usrlgn.ID
 

Sokil.Dmytro

Новичок
нельзя делать usrlgn.*, так как в GROUP BY надо перечислить все поля из USRLGN, которые указываются в селекте, а в GROUP BY понятное дело нельзя писать usrlgn.*

-~{}~ 27.09.09 03:59:

если делать джойн то получится список из неповторяющихся сессионных записей и повторяющиеся данные пользователя для каждой записи сессии. соответственно нельзя делать дистинкт, так как в каждой записи данные о датах уникальны и дистинкт ничего не отсечет

-~{}~ 27.09.09 04:00:

пока единственный адекватный вариант - это два запроса и связка полученных данных через ПХП
 

Sokil.Dmytro

Новичок
там полей двадцать а я ленив чтобы делать решения в лоб, да и что-то мне подсказывает что группировка по двадцати полям не самое лучшее решение
 

whirlwind

TDD infected, paranoid
Максиальную дату можно получить без группировки так примерно select lst_dt from xxxxx where user=yyy order by lst_dt desc limit 1 или как там в фб лимиты. Если в фб поддерживается and в джойнах, то можно запрос там на проверку lst_dt добавить. У тебя же не может быть ситуации, когда время логина младше чем время действия.

-~{}~ 27.09.09 04:10:

А вообще, стоит подумать надо ли хранить так сессии. Если тебе журнал нужден, так при чем тут сессии - делай отдельно. И не будет гемора.
 

Sokil.Dmytro

Новичок
я получаю список пользователей а не одного пользователя поэтому любые сортировки и лимиты не катят. было бы все так просто, не было бы проблем :)
 

whirlwind

TDD infected, paranoid
блин. в подзапросе

-~{}~ 27.09.09 04:12:

... LEFT JOIN session ON user=X AND время действия = (выбрать время последнего действия для юзера) ...
 

Sokil.Dmytro

Новичок
это не пхпшные сессии, назовем это сеансом работы с системой:
начало сеанса - логин
конец сеанса - выход или таймаут

надо логировать заходы пользователя в систему и показывать статистику заходов. а в общем списке пользователей - отображать время последнего захода

-~{}~ 27.09.09 04:14:

и тогда у меня будет ровно столько подзапросов, сколько юзеров в группе. а если их триста?
 

whirlwind

TDD infected, paranoid
я тебя понимаю, а ты меня? :) журнал отдельно, котлеты отдельно

-~{}~ 27.09.09 04:17:

> и тогда у меня будет ровно столько подзапросов, сколько юзеров в группе. а если их триста?

ну ты уж определись. группировка или подзапрос.

-~{}~ 27.09.09 04:20:

Вообще знаешь как бы я сделал. Я бы завел тупо таблицу с ключами юзер/посл. запись в журнале. И джойнил бы по ней когда нужно последнее действие отображать.
 

Sokil.Dmytro

Новичок
я тебя тоже прекрасно понимаю :)
все и так отдельно. я напридумывал варианты и с группировками и с подзапросами - определиться не могу, так как все не нравится. последний вариант конечно хорош - но тогда при кажом запросе надо обновлять две таблици - журнал входов и последние даты в таблице юзера
 

whirlwind

TDD infected, paranoid
Если в варианте с подзапросом сделать индекс на юзера и время действия, то подозреваю, что он будет быстрее варианта с группировкой. Увеличение колва запросов не всегда ведет к суммарному увеличению времени обработки.
 
Сверху