Recuperar registros individuales de tablas unidas que pueden producir múltiples registros
-
03-07-2019 - |
Pregunta
Tengo una mesa de estudiantes y una mesa de inscripción; un estudiante puede tener varios registros de inscripción que pueden estar activos o inactivos.
Quiero obtener una selección que tenga un solo registro de estudiante y un indicador de si ese estudiante tiene inscripciones activas.
Pensé en hacer esto en una UDF en línea que usa la identificación del estudiante para unirse a la tabla de inscripción, pero me pregunto si hay una mejor manera de hacerlo en una sola declaración de selección.
La llamada UDF podría verse algo así como:
Select Student_Name,Student_Email,isEnrolled(Student_ID) from Student
¿Cuál podría ser la alternativa, con una declaración SQL?
Solución
select Student_Name,
Student_Email,
(select count(*)
from Enrollment e
where e.student_id = s.student_id
) Number_Of_Enrollments
from Student e
obtendrá el número de inscripciones, lo que debería ayudar.
Otros consejos
¿Por qué no unirse a una selección secundaria? A diferencia de otras soluciones, esto no está activando una subconsulta por cada fila devuelta, sino que recopila los datos de inscripción para todos a la vez. La sintaxis puede no ser del todo correcta, pero debería tener una idea.
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
La selección de inscripciones también se podría rehacer como una función que devuelve una tabla para que se una.
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
Editar:
Renze de Waal corrigió mi mal SQL!
Prueba algo como esto:
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
Creo que también puedes usar la declaración de existencia en la selección pero no en la positiva
intenta evitar el uso de udfs o subconsultas, son asesinos de rendimiento. Banjolity parece tener una buena solución de lo contrario porque utiliza una tabla derivada en lugar de un UDF o una subselección.
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
Por supuesto, reemplace la decodificación con una función que usa su base de datos (o, una declaración de caso).