2017-07-25 4 views
1

Ich arbeite an einer Online-Universität, die Benutzer, Kurse und Benutzer zum Kursstatus hat. Ich habe eine Liste von Benutzern und eine andere Liste von Kursen. Ich möchte den Kursstatus für alle angegebenen Benutzer und Kurse finden, einschließlich Nullstatus für einen Kurs, den ein Benutzer noch nicht gestartet hat.MySQL-Abfrage zum Finden einer Beziehung zwischen drei Tabellen mit Einschränkungen, auch wenn die Beziehung nicht existiert

So zum Beispiel:

User IDs: [1, 7, 14, 21] 
Course IDs: [5, 8, 36, 50] 

Wunschergebnis:

Name  Course      Status 
John Doe How to Tie your Shoes  Complete 
John Doe How to Paint your House In Progress 
Jane Doe How to Tie your Shoes  Complete 
Jane Doe How to Paint your House Not Started <-- These are the tricky ones 

...

Es scheint, dass ich eine LEFT JOIN auf den Tischen durchführen können und einige NULL-Werte erhalten, Das kann ich in "Nicht gestartet" vereinigen, aber sobald ich einige Beschränkungen hinzufüge, um die Kurse und/oder Benutzer zu begrenzen, nach denen ich suche, hört es auf, NULL-Werte zu geben, weil eine NULL-Kurs-ID eindeutig nicht drin ist m y Liste der oben genannten Kurse.

Hier ist ein Beispiel Abfrage gibt Ihnen eine Vorstellung von dem, was ich habe versucht worden ist (unter anderem):

SELECT 
    `users`.`name` AS `Name`, 
    `users`.`email` AS `Email`, 
    `courses`.`number` AS `Course #`, 
    `courses`.`name` AS `Course`, 
    COALESCE(`courses_users_statuses`.`name`, 'Not Started') AS `Status` 
FROM 
    `users` 
    LEFT JOIN `courses_users` 
     ON `courses_users`.`user_id` = `users`.`id` 
    LEFT JOIN `courses` 
     ON `courses`.`id` = `courses_users`.`course_id` 
    LEFT JOIN `courses_users_statuses` 
     ON `courses_users_statuses`.`id` = `courses_users`.`status_id` 
WHERE 
    `courses`.`id` IN ([1, 2, 3, 4, 5, 10, 11, 12, 16, ...]) 
    AND `users`.`id` IN ([1, 2, 3, 4, 5, 20, 21, 36, 48, ...]) 
ORDER BY 
    `users`.`name`, 
    `courses`.`number` 

Irgendwelche Ideen auf, wie so etwas zu schreiben? Lassen Sie mich auch wissen, ob ich weitere Details oder mehr Code/Tabellen Beispiele zur Verfügung stellen kann.

EDIT: Hier ist meine aktualisierte Abfrage-Tipps von den Antworten unter Verwendung von:

SELECT 
    `users`.`name` AS `Name`, 
    `users`.`email` AS `Email`, 
    `courses`.`number` AS `Course #`, 
    `courses`.`name` AS `Course`, 
    COALESCE(`courses_users_statuses`.`name`, 'Not Started') AS `Status` 
FROM 
    `users` 
     LEFT JOIN 
    `courses_users` ON `courses_users`.`user_id` = `users`.`id` 
     LEFT JOIN 
    `courses` ON `courses`.`id` = `courses_users`.`course_id` AND `courses`.`id` IN (1, 2, 3, 4, 5) 
     LEFT JOIN 
    `courses_users_statuses` ON `courses_users_statuses`.`id` = `courses_users`.`status_id` 
WHERE 
    `users`.`id` IN (1, 2, 3, 4, 5) 
ORDER BY 
    `partners`.`name`, 
    `users`.`name`, 
    `courses`.`number` 

Dieses aktualisierte Beispiel ist eine Verbesserung, aber jetzt ist es Aufzeichnungen zeigen, wo es kein Kurs Namen oder eine Nummer, aber es gibt einen Status . Ich bin nicht sicher, wie es einen Status für Kursbeziehungen ergreift, die existieren sollten. Stattdessen sollten diese NULL (oder "Nicht gestartet") sein. Hier einige Beispieldaten aus der Datenbank:

`users` Tabelle:

id name    email 
1  Stevie McComb [email protected] 
2  John Doe   [email protected] 
3  Jane Doe   [email protected] 

`courses` Tabelle:

id number name 
1  101  Navigation 
2  102  Logging In 
3  103  Updating Records 
4  104  Managing Users 

`courses_users` Tabelle:

course_id user_id status_id completed_at 
1   1   2   2017-01-01 00:00:00 
3   1   1   2017-01-05 00:23:00 
1   2   2   2017-04-13 15:00:37 

`courses_users_statuses` Tabelle:

012.351.
id name   slug 
1  In Progress progress 
2  Complete  complete 

Gewünschtes Ergebnis:

Name    Email    Course # Course    Status 
Stevie McComb [email protected] 101   Navigation   Complete 
Stevie McComb [email protected] 102   Logging In   Not Started 
Stevie McComb [email protected] 103   Updating Records In Progress 
Stevie McComb [email protected] 104   Managing Users  Not Started 
John Doe   [email protected] 101   Navigation   Complete 
John Doe   [email protected] 102   Logging In   Not Started 
John Doe   [email protected] 103   Updating Records Not Started 
John Doe   [email protected] 104   Managing Users  Not Started 
Jane Doe   [email protected] 101   Navigation   Not Started 
Jane Doe   [email protected] 102   Logging In   Not Started 
Jane Doe   [email protected] 103   Updating Records Not Started 
Jane Doe   [email protected] 104   Managing Users  Not Started 

Aktuell Ergebnis:

Name    Email    Course # Course    Status 
Stevie McComb [email protected]         Complete 
Stevie McComb [email protected]         Not Started 
Stevie McComb [email protected] 103   Updating Records In Progress 
Stevie McComb [email protected]         Not Started 
John Doe   [email protected] 101   Navigation   Complete 
John Doe   [email protected]         Not Started 
John Doe   [email protected]         Not Started 
John Doe   [email protected]         Not Started 
Jane Doe   [email protected]         Not Started 
Jane Doe   [email protected]         Not Started 
Jane Doe   [email protected]         Not Started 
Jane Doe   [email protected]         Not Started 

Antwort

0

Das Problem ist, dass Sie die Einschränkungen in where-Klausel setzen sind, ohne .. für einen null Wert erlaubt Diese ändert im Wesentlichen die left join zurück zu inner join.

Um dies zu beheben, können Sie entweder Nullen explizit zulassen, oder Sie können die Logik in die Join-Klausel verschieben (meine Präferenz).

select 
    `users`.`name` as `name`, 
    `users`.`email` as `email`, 
    `courses`.`number` as `course #`, 
    `courses`.`name` as `course`, 
    coalesce(`courses_users_statuses`.`name`, 'not started') as `status` 
from 
    `users` 
    left join `courses_users` 
     on `courses_users`.`user_id` = `users`.`id` 
    left join `courses` 
     on `courses`.`id` = `courses_users`.`course_id` 
     and `courses`.`id` in ([1, 2, 3, 4, 5, 10, 11, 12, 16, ...]) 
    left join `courses_users_statuses` 
     on `courses_users_statuses`.`id` = `courses_users`.`status_id` 
where 
    `users`.`id` in ([1, 2, 3, 4, 5, 20, 21, 36, 48, ...]) 
order by 
    `users`.`name`, 
    `courses`.`number` 
+0

Ich habe total vergessen, dass Sie Einschränkungen hinzufügen können zu einem Beitritt. Das könnte das Problem lösen. Ich werde das heute ausprobieren, sobald ich eine Änderung habe, um wieder an dem Projekt zu arbeiten und Sie wissen zu lassen, ob ich irgendwelche anderen Probleme habe. –

+0

@StevieMcComb: Wenn dies Ihre Frage beantwortet, denken Sie bitte daran, die Antworten entsprechend zu übernehmen. Ich erwähne das nur, weil du neu bist und vielleicht nicht weißt (https://stackoverflow.com/help/someone-answers). – Jacobm001

+0

Entschuldigung für die Verspätung, ich hatte bis heute Morgen keine Chance, an diesem Projekt zu arbeiten. Das hat mich näher gebracht, da es in Bereichen, in denen es vorher nicht war, Nullwerte liefert ... aber jetzt macht es mir neue Kopfschmerzen. Es werden Datensätze zurückgegeben, denen zufällig die Kursnamen und/oder -nummern fehlen. Dies geschieht sogar, wenn der Kursstatus "Complete" oder "In Progress" ist, was bedeutet, dass dort kein Nullwert vorhanden ist, aber aus irgendeinem Grund fehlen die Kursdetails. Zum Beispiel: Name => 'John Doe', Kurs # =>, Kursname =>, Status => 'Complete' –

0

Wenn ich mit potenziell NULL-Werten zu tun haben, verwende ich

IFNULL(field_name, 'use this value instead'); 

Zum Beispiel

SELECT 
IFNULL(Course, 'Nothing Found') AS course 
FROM 
Course 
Where.... 

Oder Sie könnten in Ihrer WHERE-Klausel angeben ...

WHERE Course IS NOT NULL 
Verwandte Themen