2009-03-03 7 views
2

Ich habe eine Schüler-Tabelle und eine Registrierungstabelle; Ein Student könnte mehrere Registrierungsdatensätze haben, die aktiv oder inaktiv sein können.Einzelne Datensätze aus verknüpften Tabellen zurückholen, die mehrere Datensätze erzeugen können

Ich möchte eine Auswahl erhalten, die einen einzigen Schüler-Datensatz und einen Indikator hat, ob dieser Schüler aktive Anmeldungen hat.

Ich dachte darüber nach, dies in einer Inline-UDF zu tun, die die Studenten-ID in einem Join zur Registrierungstabelle verwendet, aber ich frage mich, ob es eine bessere Möglichkeit gibt, es in einer einzigen Select-Anweisung zu tun.

Das UDF-Aufruf könnte etwa so aussehen:

Select Student_Name,Student_Email,isEnrolled(Student_ID) from Student 

Was könnte die Alternative - mit einer SQL-Anweisung - aussehen?

Antwort

1
select Student_Name, 
     Student_Email, 
     (select count(*) 
     from Enrollment e 
     where e.student_id = s.student_id 
     ) Number_Of_Enrollments 
from Student e 

wird die Anzahl der Einschreibungen erhalten, die helfen sollte.

+0

Wenn Sie 1000 Studenten hatten, ist diese Anweisung wie 1001 Abfragen, da sie die Anzahl der Anmeldungen jedes Schülers separat abfragt. – banjollity

+0

Nein, es ist nicht so schlimm. Die Datenbank kann optimieren, besonders wenn student_id der erste Teil eines Registrierungsindexes ist (was ich denke, dass es sein wird). Die Unterabfrage muss nur eine Indexbereichsüberprüfung für den Registrierungsindex durchführen (die Tabelle selbst wird nicht benötigt). –

0

Try someting wie folgt aus:

SELECT Student_Name, Student_Email, CAST((SELECT TOP 1 1 FROM Enrollments e WHERE e.student_id=s.student_id) as bit) as enrolled FROM Student s 

Ich denke, man kann auch die Aussage in der Auswahl existiert verwenden, aber nicht positiv

1

Warum nicht zu einer sekundären Auswahl verbinden? Im Gegensatz zu anderen Lösungen wird damit nicht für jede zurückgegebene Zeile eine Unterabfrage ausgelöst, sondern die Registrierungsdaten werden für alle gleichzeitig gesammelt. Die Syntax ist vielleicht nicht ganz korrekt, aber Sie sollten die Idee bekommen.

SELECT 
    s.student_name, 
    s.student_email, 
    IsNull(e.enrollment_count, 0) 
FROM 
    Students s 
LEFT OUTER JOIN (
     SELECT 
      student_id, 
      count(*) as enrollment_count 
     FROM 
      enrollments 
     WHERE 
      active = 1 
     GROUP BY 
      student_id 
    ) e 
ON s.student_id = e.student_id 

Die Auswahl von Einschreibungen auch als Funktion nochmals gemacht werden könnten, die eine Tabelle zurückgibt für Sie zu verbinden.

CREATE FUNCTION getAllEnrollmentsGroupedByStudent() 
RETURNS @enrollments TABLE 
(
    student_id  int, 
    enrollment_count int 
) AS BEGIN 
    INSERT INTO 
     @enrollments 
    (
     student_id, 
     enrollment_count 
    ) SELECT 
     student_id, 
     count(*) as enrollment_count 
    FROM 
     enrollments 
    WHERE 
     active = 1 
    GROUP BY 
     student_id 

    RETURN 
END 


SELECT 
    s.student_name, 
    s.student_email, 
    e.enrollment_count 
FROM 
    Students s 
JOIN 
    dbo.getAllEnrollmentsGroupedByStudent() e 
ON s.student_id = e.student_id 

Edit:
Renze korrigiert de Waal meine schlechte SQL!

+0

Beachten Sie, dass der Beitritt, wenn ein Schüler keine Einschreibungen hat, diese Studenten verlassen würde. Ein linker Join würde helfen, aber enrollment_count kann null sein. –

+0

Guter Ort. Ich habe ein paar Änderungen vorgenommen. – banjollity

0

versuchen, UDFs oder Unterabfragen zu vermeiden, sind sie Performance-Killer. banjolity scheint sonst eine gute Lösung zu haben, weil es statt einer UDF oder subselect eine derived Tabelle verwendet.

0
select students.name, 
decode(count(1), 0, "no enrollments", "has enrollments") 
    from students, enrollments 
    where 
     students.id = enrollments.sutdent_id and 
     enrollments.is_active = 1 group by students.name 

Ersetzen Sie die Dekodierung natürlich mit einer Funktion, die Ihre Datenbank verwendet (oder eine Case-Anweisung).

Verwandte Themen