2016-08-31 4 views
5

Als Ergebnis trying to make a Slick query more readable, ich habe diese Abfrage Konstruktor, der wäre gleichbedeutendWarum sind diese beiden Slick-Abfragen nicht gleichwertig?

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) 
} 

und dieses Ich dachte, funktioniert, aber nicht funktioniert:

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) 
} 

Die zweite scheint eine Liste von Profilen zurückgeben, die mehr als das Zielprofil enthalten.

Warum sind sie nicht gleich?


Die "gleichwertig" SQL (IE das Ziel dieser Konstruktion) ist:

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) 

Antwort

3

Das Problem hier ist, slick uses a LiteralNode(true) as the default join condition. also die zweite Abfrage werden die Ergebnisse in etwas sieht wie folgt aus:

Wie Sie sehen können, sind alle Bedingungen, die als Join-Bedingung für jede der verbundenen Tabellen erwartet werden, tatsächlich Teil der Join-Bedingung des letzten Joins.

Um zu verstehen, wie diese das Endergebnis beeinflussen, lassen Sie sich das Problem vereinfachen, wie folgt:

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; 

Am ersten join, a1 und a2 durch eine Bedingung verbunden, die immer wahr ist, in einem temporären Ergebnis resultierenden von:

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

Jetzt betrachten wir die zweite Verbindung. Wir haben a1.id = a2.id als Teil der Join-Bedingung, aber denken Sie daran, dass die Join-Bedingung verwendet wird, um zu entscheiden, wie Zeilen aus Tabelle a3 abgerufen werden, nicht um das Zwischenergebnis des ersten Joins zu filtern. Und wir machen hier einen linken Join, so dass eine zusätzliche a3-Zeile von NULL erzeugt wird, selbst wenn die Join-Bedingung nicht erfüllt ist. Das endgültige Ergebnis wird sein:

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

So, du bist viel mehr erwartet unerwartete Ergebnisse mit Spalten der letzten linken verknüpften Tabelle sehen NULL.

Verwandte Themen