2017-01-25 5 views
0

Ich habe die folgenden Tabellen in einer Laravel Anwendung:SQL - Inner Join Reihen auszuschließen

basket 
------ 
id PK 
user_id FK 
coupon_id FK 

basket_items 
------------ 
id PK 
basket_id FK 
product_id FK 
quantity 
purchaseable_id 
purchaseable_type -- e.g. App\Job, App\CV 

products 
-------- 
id PK 
name -- e.g. standard job listing, premium job listing, cv download 
price 

jobs 
---- 
id PK 
product_id FK 
title 
start_dt 
expiry_dt 
description 

cvs 
------ 
id PK 
name 

Die purchaseable_id und purchaseable_type in der basket_items Tabelle sind polymorph Beziehungen. Z.B. Ein Benutzer kann seinem Warenkorb einen Job hinzufügen, bei dem es sich um einen Premium-Produkteintrag handelt - die käufliche_ID wäre in diesem Fall die ID des Jobs aus der Jobtabelle und der käufliche_Typ wäre App\Job. Beispielzeilen in der basket_items Tabelle kann wie folgt aussehen:

id | basket_id | product_id | quantity | purchaseable_id | purchaseable_type 
---------------------------------------------------------------------------- 
1 | 1   | 1   | 1  |  1   | App\Job 
2 | 1   | 3   | 1  |  1   | App\CV 

ich alle basket_items für einen Benutzer auflisten möchten aber Gegenstände auszuschließen, wenn der purchaseable_type App\Job ist und der Job ist abgelaufen.

Ich habe folgendes versucht, aber es schließt alle Zeilen, wenn der Auftrag abgelaufen ist, dh es Elemente von jedem purchaseable_type schließt

select t1.*, t2.* 
from baskets t1 
inner join basket_items t2 on t2.basket_id = t1.id 
left join jobs t3 on t3.id = t2.purchaseable_id and t2.purchaseable_type = 'App\\Job' 
where t1.user_id = 1 and t3.expiry_dt > now() 
+0

Bearbeiten Sie Ihre Frage und liefern Sie Beispieldaten und gewünschte Ergebnisse. –

Antwort

0

Wenn Sie eine links, um alle Felder in der Tabelle verbinden, die nicht aufeinander abgestimmt sind durch die Join-Bedingung sind NULL. Wenn in diesem Fall Ihre Verbindung von basket_items zu jobs nicht mit der Join-Bedingung übereinstimmt, sind die von jobs gezogenen Felder alle NULL. Da der Ausdruck immer false zurückgibt, wenn t3.expiry_dtNULL ist, werden Sie diese Datensätze nicht zurückbekommen.

Ein Weg, um es Ihre WHERE-Klausel zu ändern, um die NULL-Werte zu ermöglichen:

where t1.user_id = 1 and (t3.expiry_dt > now() OR t3.expiry_dt IS NULL) 
0

Sie die Arbeitsplätze Tabelle nicht beitreten. Sie wollen es nur als Kriterien verwenden, so gehört es in WHERE oder ON:

select * 
from baskets b 
join basket_items bi 
    on bi.basket_id = b.id 
    and not 
    (
    bi.purchaseable_type = 'App\Job' and 
    bi.purchaseable_id in (select id from jobs where expiry_dt > now()) 
) 
where b.user_id = 1; 

Dies wird Ihre Anfrage besser lesbar und damit weniger anfällig für (zukünftige) Fehler, das heißt bessere Wartbarkeit bietet.

+0

Ihre Lösung macht zwei Abfragen, die ineffizient sind. Der Warenkorb muss nur eine linke Verknüpfung durchführen, wenn sich Jobartikel im Warenkorb befinden. Es ist vielleicht besser lesbar, aber weniger effizient. Stellen Sie sich vor, ich hätte Tausende von Arbeitsplätzen. – adam78

+0

Nein. Die Abfrage funktioniert genauso, so dass ein DBMS den gleichen Ausführungsplan verwenden kann. Es läuft darauf hinaus, wie das DBMS die Dinge intern handhabt und wir sollten nicht zu viele Annahmen darüber machen (besonders da sich dies von Version zu Version ändern kann). Die Job-Unterabfrage muss nur einmal ausgeführt werden und ihr Ergebnis muss nur für die wenigen Datensätze von Benutzer 1 mit einem käuflichen_typ = "App \ Job" gesucht werden. Das sollte schnell gehen. Schreiben Sie Ihre Abfragen so leserlich wie möglich. Nur wenn Sie tatsächlich Probleme haben, versuchen Sie Wege um sie herum (z. B. Ihre halbe Anti-Join). –

+0

Aus der Sicht der Leistung: Ihre Outer Join bekommt Sie mehr Datensätze dann benötigt (die Sie in der Where-Klausel ablehnen). Wenn also das DBMS Ihren Anweisungen direkt folgt (was es intern nicht tun muss), wird es ein größeres Zwischenergebnis als nötig erzeugen, was vermieden werden sollte. Aber wie bereits erwähnt, sollte der DBMS-Optimierer in der Lage sein, eine Abfrage zu beurteilen, die Risiken zu erkennen und sich für den besten Ausführungsplan zu entscheiden. –