2016-08-31 4 views
5

В результате trying to make a Slick query more readable, у меня есть этот конструктор запросов, который работаетПочему эти два запроса Slick не эквивалентны?

val q = Users.filter(_.id === userId) join People on { 
    case (u, p) => u.personId === p.id 
} joinLeft Addresses on { 
    case ((u, p), a) => p.addressId === a.id 
} joinLeft Businesses on { 
    case (((u, p), a), b) => p.businessId === b.id 
} joinLeft Addresses on { 
    case ((((u, p), a), b), ba) => b.flatMap(_.addressId) === ba.id 
} map { 
    case ((((u, p), a), b), ba) => (p, a, b, ba) 
} 

И этот я думал, что было бы эквивалентно, но не работает:

val q = Users.filter(_.id === userId) join People joinLeft Addresses joinLeft Businesses joinLeft Addresses on { 
    case ((((u, p), a), b), ba) => 
    u.personId === p.id && 
    p.addressId === a.flatMap(_.id) && 
    p.businessId === b.flatMap(_.id) && 
    b.flatMap(_.addressId) === ba.id 
} map { 
    case ((((u, p), a), b), ba) => (p, a, b, ba) 
} 

Второй по-видимому, возвращать список профилей, который содержит больше целевого.

Почему они не то же самое?


«эквивалентного» SQL (IE цель этой конструкции) является:

select p.*, a1.*, b.*, a2.* from Users u 
innerJoin People p on (u.personId == p.id) 
leftJoin Addresses a1 on (p.addressId == a1.id) 
leftJoin Businesses b on (p.businessId == b.id) 
leftJoin Addresses a2 on (b.addressId == a2.id) 

ответ

3

Проблема здесь в том, slick uses a LiteralNode(true) as the default join condition. Так что второй запрос будет результаты в чем-то выглядит следующим образом:

select p.*, a1.*, b.*, a2.* 
    from Users u 
    join People p on 1 = 1 
left join Addresses a1 on 1 = 1 
left join Businesses b on 1 = 1 
left join Addresses a2 on u.personId = p.id 
         and p.addressId = a1.id 
         and p.businessId = b.id 
         and b.addressId = a2.id 

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

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

create temporary table A (id int primary key); 

insert into A values (1), (2); 

    select a1.id, a2.id, a3.id 
    from A a1 
    join A a2 on 1 = 1 
left join A a3 on a1.id = a2.id 
       and a2.id = a3.id; 

На первом Join, a1 и a2 соединены условием, что всегда верно, что приводит к временному результату of

(1, 1) 
(1, 2) 
(2, 1) 
(2, 2) 

Теперь давайте рассмотрим второе соединение. У нас есть a1.id = a2.id как часть условия соединения, но помните, что условие соединения используется для определения того, как извлекать строки из таблицы a3, а не для фильтрации промежуточного результата первого соединения. И мы делаем левое соединение здесь, поэтому генерируется дополнительная строка A3 из NULL, даже если условие соединения не выполняется. Окончательный результат будет:

(1, 1, 1) 
(1, 2, NULL) 
(2, 1, NULL) 
(2, 2, 2) 

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