Странная проблема с left join

Запросы, планы, оптимизация запросов, ...

Модераторы: kdv, CyberMax

Ответить
ARM
Сообщения: 26
Зарегистрирован: 02 дек 2006, 13:27

Странная проблема с left join

Сообщение ARM » 30 июн 2007, 14:49

Наткнулся на проблему, которая меня поставила в штопор :shock:
Есть 2 таблицы:
- Пользователи (Users), где кроме других полей есть UserId
- Таблица логирования (log_UserSession), где есть также UserId, по кот. создан внешний ключ с Users.UserId
В таблице Users есть, допустим одна запись с UserId=1.
В таблице логирования есть 2 записи с UserId=1.
Делаю элементарный запрос:
select
u.userid
from users u
left join log_usersession us on us.userid=u.userid

и в результате получаю 2 записи с UserId=1 вместо 1-й из Users. :shock:

Может я не произнес какое-то заклинание ? В MS SQL отрабатывает на ура....

ЗЫ: FB 2.1 Alpha (может в Альфе проблема ?)

Attid
Спец
Сообщения: 377
Зарегистрирован: 14 ноя 2006, 09:58

Сообщение Attid » 01 июл 2007, 09:27

ну все правельно ты ведь их джойнишь, distict сделай если не нравится 2 записи =)

ARM
Сообщения: 26
Зарегистрирован: 02 дек 2006, 13:27

Сообщение ARM » 01 июл 2007, 12:34

Да нет, не правильно, join то левый! Т.е. все записи из Users, и только те из Лога, кот. есть в Users.

mdfv
Сообщения: 119
Зарегистрирован: 23 май 2006, 15:53

Сообщение mdfv » 01 июл 2007, 17:47

По моему вы перемудрили, результат правильный выдан.
вместо

Код: Выделить всё

select
u.userid 
напишите

Код: Выделить всё

select
u.userid, us.* 
суть не изменится, а смысл появится.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Re: Странная проблема с left join

Сообщение WildSery » 01 июл 2007, 20:38

ARM писал(а):Может я не произнес какое-то заклинание ? В MS SQL отрабатывает на ура...
Чего врёшь-то? Не наговаривай на MSSQL такое.
Любой джойн выводит количество строк по одному условию, равное произведению числа строк в первой и второй, либо одну, в случае отсутствия и внешнего соединения.
Загляни, пожалуй, в Грабера Мартина, почитай про соединения.

ARM
Сообщения: 26
Зарегистрирован: 02 дек 2006, 13:27

Re: Странная проблема с left join

Сообщение ARM » 02 июл 2007, 12:56

Спасибо, консерваторию подправил, век живи - век учись.
Помоги, плиз, еще с одной проблемой: есть все та же таблица пользователей (Users) и логирования (Log_UserSession). В пользователях поле UserId, в логировании - UserId (foreign key с Users.UserId), DateLogin, DateLogoff.
Нужно выбрать всех пользователей, а также дату последнего логирования (DateLogin) пользователя с условием, что пользователь еще не отключился (DateLogoff=null). Записей c DateLogoff=null может быть много (клиент отвалился по ошибке), поэтому нужно взять самую позднюю дату логирования + DateLogoff=null!!!
Я это вижу так:
select
U.UserId,
US.DateLogin
from Users U
left join
(
select
first 1
DateLogin
from Log_UserSession US
where DateLogoff is null and US.UserId = U.UserId
order by DateLogin desc
) as US(DateLogin)

НО поля внешней (в соединении) таблицы в подзапросе использовать нельзя, т.е.:
where DateLogoff is null and US.UserId = U.UserId
выделенное не пройдет.

За ранее спасибо.

mdfv
Сообщения: 119
Зарегистрирован: 23 май 2006, 15:53

Сообщение mdfv » 02 июл 2007, 13:26

Дык так и можно.

Код: Выделить всё

select
U.UserId,
(
select
first 1
DateLogin
from Log_UserSession US
where DateLogoff is null and US.UserId = U.UserId
order by DateLogin desc
) as DateLogin
from Users U


ARM
Сообщения: 26
Зарегистрирован: 02 дек 2006, 13:27

Сообщение ARM » 02 июл 2007, 13:30

mdfv писал(а):Дык так и можно.

Код: Выделить всё

select
U.UserId,
(
select
first 1
DateLogin
from Log_UserSession US
where DateLogoff is null and US.UserId = U.UserId
order by DateLogin desc
) as DateLogin
from Users U

Как вариант "только для выборки даты логирования" пойдет, НО вместе с датой логирования нужно выбрать другие данные из табл. логирования (надо было мне упомянуть), т.е. нужно рулить все таки в сторону подзапроса...

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 02 июл 2007, 14:49

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

Код: Выделить всё

for select UserID from Users into UserId
do begin
  DateLogin = null;
  select first 1 DateLogin 
    from Log_UserSession
    where DateLogoff is null and UserId = :UserId
    order by DateLogin desc
    into DateLogin;
  suspend;
end

ARM
Сообщения: 26
Зарегистрирован: 02 дек 2006, 13:27

Сообщение ARM » 02 июл 2007, 14:54

WildSery писал(а):Подзапросом можно, но это будет некрасиво и серверу опять же в напряг.
Сделай лучше хранимой процедурой.

Код: Выделить всё

for select UserID from Users into UserId
do begin
  DateLogin = null;
  select first 1 DateLogin 
    from Log_UserSession
    where DateLogoff is null and UserId = :UserId
    order by DateLogin desc
    into DateLogin;
  suspend;
end
Спасибо, то что нужно.
А слона то я и не заметил... :?

Ответить