SQL - Consulta SQL; Listar TODOS los alumnos que cursen determinado curso.

 
Vista:

Consulta SQL; Listar TODOS los alumnos que cursen determinado curso.

Publicado por Agustin Tomas Larghi (2 intervenciones) el 05/11/2011 14:40:46
Hola, hace ya un día que estoy peliando con la misma f0cking consulta de SQL, es sencillo pero a la vez muy molesto porque todas las soluciones que me han propuesto mis profesores y mis compañeros tiran resultados erroneos. Realmente simple, es lo siguiente:

Listar nombre y apellido de los alumnos que hayan cursado todos los cursos.

Acá tienen la tabla para hacerse una idea.

PERSONA = (DNI, Apellido, Nombre, Fecha_Nacimiento, Estado_Civil, Genero)
ALUMNO = (DNI, Legajo, Año_Ingreso)
PROFESOR = (DNI, Matricula, Nro_Expediente)
TITULO = (Cod_Titulo, Nombre, Descripción)
TITULO-PROFESOR = (Cod_Titulo, DNI, Fecha)
CURSO = (Cod_Curso, Nombre, Descripción, Fecha_Creacion, Duracion)
ALUMNO-CURSO = (DNI, Cod_Curso, Año, Desempeño, Calificación)
PROFESOR-CURSO = (DNI, Cod_Curso, Fecha_Desde, Fecha_Hasta)

Yo ya trate con los siguientes razonamientos

1) Usar conjuntos; saco el conjunto de los alumnos que no cursaron ninguna materia y se lo resto al conjunto de alumnos en general, con un pequeño problema; el complemento de NINGUNO no es TODOS, sino ALGUNOS

2) Usar NOT EXISTS; Lo encare asi "Los alumnos que no hayan no cursado ninguna materia", el resultado es el mismo que con conjuntos, me devuelve los alumnos que hayan cursado al menos 1 materia

3) Usar COUNT; este si funciona, pero es recontra ineficiente, ya que ha recibido tantas criticas estoy un poco desesperado por encontrar una consulta que haga lo que el enunciado pide y no use COUNT

Si ha alguno de ustedes se les ocurre alguna idea, se los agradecería un montón, además cualquier código que se les venga a la mente, si quieren lo publican y yo lo tiro en la base de datos y les digo que tal salio.

Muchas gracias por detenerse a leer.

Aproposito, acá les dejo el link del tema donde publique el código del COUNT:
http://foro.elhacker.net/desarrollo_web/consulta_sqllistar_elementos_que_estan_presentes_en_todos_los_elementos_de_otra-t343529.0.html
Valora esta pregunta
Me gusta: Está pregunta es útil y esta claraNo me gusta: Está pregunta no esta clara o no es útil
0
Responder
sin imagen de perfil
Val: 806
Bronce
Ha mantenido su posición en SQL (en relación al último mes)
Gráfica de SQL

Consulta SQL; Listar TODOS los alumnos que cursen determinado curso.

Publicado por leonardo_josue (1173 intervenciones) el 07/11/2011 22:29:04
Hola Agustin Tomas Larghi:

No entiendo por qué dices que utilizar COUNT es "recontra ineficiente"... en lo particular creo que justamente esta es la manera más eficiente de hacer la consulta que planteas, lo único que debes tener cuidado sería en tener un buen manejo de índices...

Ahora bien, si insistes no utilizar la función COUNT se me ocurre otra solución, y es crear un producto cartesiano entre tus tablas ALUMNOS y CURSOS para obtener todas las combinaciones que deberían existir en tu tabla ALUMNOS_CURSOS, de tal suerte que si no existe, entonces quiere decir que ese alumno NO HA TOMADO CIERTO CURSO... suena medio complicado, pero veamos si con el script queda un poco más claro mi idea:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
mysql> CREATE TABLE alumnos (idAlumno INT, nombre VARCHAR(10));
Query OK, 0 rows affected (0.12 sec)
 
mysql> INSERT INTO alumnos VALUES (1, 'uno'), (2, 'dos'), (3, 'tres'),
    -> (4, 'cuatro');
Query OK, 4 rows affected (0.05 sec)
Records: 4  Duplicates: 0  Warnings: 0
 
mysql> SELECT * FROM alumnos;
+----------+--------+
| idAlumno | nombre |
+----------+--------+
|        1 | uno    |
|        2 | dos    |
|        3 | tres   |
|        4 | cuatro |
+----------+--------+
4 rows in set (0.00 sec)
 
mysql> CREATE TABLE cursos (idCurso INT, descripcion VARCHAR(15));
Query OK, 0 rows affected (0.12 sec)
 
mysql> INSERT INTO cursos VALUES (1, 'matemáticas'), (2, 'español'),
    -> (3, 'historia'), (4, 'química');
Query OK, 4 rows affected (0.03 sec)
Records: 4  Duplicates: 0  Warnings: 0
 
mysql> SELECT * FROM cursos;
+---------+-------------+
| idCurso | descripcion |
+---------+-------------+
|       1 | matemáticas |
|       2 | español     |
|       3 | historia    |
|       4 | química     |
+---------+-------------+
4 rows in set (0.00 sec)
 
mysql> CREATE TABLE alumnos_cursos (idAlumnoCurso INT, idAlumno INT, idCurso INT
);
Query OK, 0 rows affected (0.10 sec)
 
mysql> INSERT INTO alumnos_cursos VALUES
    -> (1, 1, 1),(2, 1, 2),(3, 1, 3),(4, 2, 1),(5, 2, 2),(6, 2, 3),
    -> (7, 2, 4),(8, 3, 1),(9, 3, 1),(10, 3, 2),(11, 3, 3);
Query OK, 11 rows affected (0.02 sec)
Records: 11  Duplicates: 0  Warnings: 0
 
mysql> SELECT * FROM alumnos_cursos;
+---------------+----------+---------+
| idAlumnoCurso | idAlumno | idCurso |
+---------------+----------+---------+
|             1 |        1 |       1 |
|             2 |        1 |       2 |
|             3 |        1 |       3 |
|             4 |        2 |       1 |
|             5 |        2 |       2 |
|             6 |        2 |       3 |
|             7 |        2 |       4 |
|             8 |        3 |       1 |
|             9 |        3 |       1 |
|            10 |        3 |       2 |
|            11 |        3 |       3 |
+---------------+----------+---------+
11 rows in set (0.00 sec)


Observa con estos datos que pongo de ejemplo, el alumno 1 no ha tomado el curso de 4, el alumno 2 ha tomado los cuatro cursos, el alumno 3 ha tomado 4 cursos, pero el curso 1 lo ha tomado dos veces, por lo tanto también le falta tomar el curso 4. El alumno 4 no ha tomado ningún curso. Tú podrías hacer lo siguiente:

1
2
3
4
5
6
7
8
9
10
11
12
mysql> SELECT * FROM alumnos WHERE idAlumno NOT IN
    -> (
    -> SELECT DISTINCT A.idAlumno FROM alumnos A, cursos C WHERE NOT EXISTS
    -> (SELECT * FROM alumnos_cursos AC
    -> WHERE A.idAlumno = AC.idAlumno AND C.idCurso = AC.idCurso)
    -> );
+----------+--------+
| idAlumno | nombre |
+----------+--------+
|        2 | dos    |
+----------+--------+
1 row in set (0.00 sec)


Observa que en la consulta interna hago un producto cartesiano entres alumnos cursos obtengo aquellas parejas que no existen en alumnos_cursos, si ejecutas sólo la consulta interna te dará justamente la lista de los curso que los alumnos les falta tomar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> SELECT * FROM alumnos A, cursos C WHERE NOT EXISTS
    -> (SELECT * FROM alumnos_cursos AC
    -> WHERE A.idAlumno = AC.idAlumno AND C.idCurso = AC.idCurso);
+----------+--------+---------+-------------+
| idAlumno | nombre | idCurso | descripcion |
+----------+--------+---------+-------------+
|        4 | cuatro |       1 | matemáticas |
|        4 | cuatro |       2 | español     |
|        4 | cuatro |       3 | historia    |
|        1 | uno    |       4 | química     |
|        3 | tres   |       4 | química     |
|        4 | cuatro |       4 | química     |
+----------+--------+---------+-------------+
6 rows in set (0.00 sec)


Por lo tanto, utilizar esta consulta para filtrar los alumnos. Dale un vistazo para ver si es más eficiente o no que la opción del COUNT y nos comentas los resultados.

Saludos
Leo.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar
sin imagen de perfil
Val: 806
Bronce
Ha mantenido su posición en SQL (en relación al último mes)
Gráfica de SQL

Consulta SQL; Listar TODOS los alumnos que cursen determinado curso.

Publicado por leonardo_josue (1173 intervenciones) el 07/11/2011 22:39:40
Olvidé pedirte que nos digas con que motor de BD estás trabajando, ya que algunos proporcionan funciones especiales para el manejo de conjunto (EXCEPT, MINUS, INTERSECT, ETC). La solución que pongo sería utilizando sólo SQL Estándar y por tanto debería funcionar para todos los motores, pero sería conveniente que nos digas con qué estás trabajando, es posible que existan algunas otras maneras más óptimas para llegar al mismo resultado.

Saludos
Leo.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Consulta SQL; Listar TODOS los alumnos que cursen determinado curso.

Publicado por Agustin (2 intervenciones) el 07/11/2011 22:57:32
Hola y muchas gracias por responder.

Justamente estoy trabajando con el SQL estandar.

Por suerte tu solucion me sirvio bastante estaba tratando de hacer una solucion alternativa usando NOT EXISTS, pero no la podia sacar, por suerte tu manera es justo la que necesitaba destrabar.

Acerca de porque digo que es recontra ineficiente usar COUNT es por lo siguiente;
Me lo señalo mi profesor, afirmando que la manera correcta era usando NOT EXISTS encarandolo de manera que detalle en el inciso 2.

Muchas gracias Leo, saludos.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar
sin imagen de perfil
Val: 806
Bronce
Ha mantenido su posición en SQL (en relación al último mes)
Gráfica de SQL

Consulta SQL; Listar TODOS los alumnos que cursen determinado curso.

Publicado por leonardo_josue (1173 intervenciones) el 08/11/2011 18:58:41
No estoy totalmente de acuerdo con la afirmación de tu profesor, acerca de que la manera "correcta" de hacerlo es usando NOT EXISTS, por experiencia propia te puedo decir que manejar funciones de agrupación en tablas enormes (y al decir enormes me refiero a varios millones de registros) suele ser mucho más rápido que manejar todos los registros, de hecho una de las mejores prácticas al escribir consultas es la de evitar usar sentencias NOT IN y NOT EXISTS en la medida de lo posible, ya que estas consultas implican tener que revisar todos los registros cada vez, lo que puede afectar el rendimiento. Es por eso que hacía énfasis en el buen manejo de los índices, independientemente que lo hagas con COUNT o con NOT EXISTS. Finalmente, sería un buen ejercicio que probaras las dos consultas, he inclusive que preguntaras a tu profesor si él tiene una lógica diferente para llegar al mismo resultado y probar todas las consultas en el servidor, con un número considerable de registros, para ver cómo se comportan y puedas entonces decidirte por una de ellas.

Afortunadamente la mayoría de los problemas en BD tienen más de una solución, tal como diría otro compañero forista "hay muchas formas de matar una mosca, pero usar una pistola puede resultar excesivo". Haz la prueba y nos comentas los resultados.

Saludos
Leo.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Consulta SQL; Listar TODOS los alumnos que cursen determinado curso.

Publicado por Orlando (1 intervención) el 25/07/2018 05:20:53
Muy agradecido, busqué muchísimo por días... Gracias por compartir sus conocimientos
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Consulta SQL; Listar TODOS los alumnos que cursen determinado curso.

Publicado por Carlos Cuyé (1 intervención) el 07/11/2011 23:18:32
Tengo una idea de como resolver tu problema.
Te aclaro que es un teorización basada en conjuntos, por lo que deberías comprender la idea para poder depurar el código. Yo sólo te paso la idea.
Si la aplicás, creo que sería conveniente que las utilizaras mediante vistas (View).
Analizá la idea, pero si encontrás alguna cláusula que te resuelva de manera simple el problema y en un solo comando, bienvenida sea! Suerte!

Esta consulta te devolvería la totalidad de combinaciones [DNI, Cód_Curso] que podrían formarse entre Alumnos y Cursos, sin importar que los Alumnos hayan hecho o no los Cursos. Sería tu 'Universo'.
* Se usa SIN la cláusula WHERE, quizás no sea compatible en todos los motores.
Devolvería una cantidad de registros igual a la multiplicación de las cantidades de cada archivo.
El valor 1000, es una unidad seguida de tantos ceros como la cant máxima de dígitos vaya a tener el campo Cod_Curso. Es una emulación de concatenación de ambos campos pero en formato numérico.
SELECT
Alumnos.dni*1000+Cursos.Cod_Curso AS CombAluCur
Alumnos.dni,
Cursos.Cod_Curso
FROM
Alumnos,
Cursos
INTO CURSOR Universo

Esta consulta te devolvería el conjunto de combinaciones [DNI , Cod_Curso] que se forman a partir de los cursos registrados en la tabla AlumnosCursos, es decir, los cursos efectivamente realizados, y obviamente los alumnos que los cursaron.
SELECT
Alumnos.dni*1000+Cursos.Cod_Curso AS CombAluCur,
Alumnos.dni,
Cursos.Cod_Curso
FROM
Alumnos,
Cursos,
AlumnosCursos
WHERE Alumnos.dni=AlumnosCursos.dni AND
Cursos.Cod_Curso=AlumnosCursos.Cod_Curso
INTO CURSOR CursosRealizados


Esta consulta te devolvería las combinaciones de tu 'universo' que aún NO se utilizaron, es decir, todos los alumnos que no hayan cursado alguno o todos los cursos y a la vez todos los cursos que no hayan sido relacionados aún con los registros de alumnos.

SELECT
CombAluCur
FROM
Universo
WHERE Universo.CombAluCur NOT IN (SELECT CombAluCur FROM CursosRealizados)
INTO CURSOR CombNoUtilizadas


Si analizás como conjuntos las consultas anteriores, verás que la unión de las dos últimas es igual al conjunto universal -> Universo = CursosRealizados U CombNoUtilizadas

Ahora bien, lo que a vos te interesa encontrar son los Alumnos que están relacionados con el conjunto CursosRealizados pero que NO están relacionados con el conjunto CombNoUtilizadas, que es el caso de los alumnos que realizan todos los cursos, ya que sus códigos al combinarse con todos los códigos de cursos existentes (por cursarlos a todos), quedan relacionados sólamente con el conjunto CursosRealizados.
Esto sería compatible con la siguiente consulta:
*** Importante la cláusula DISTINCT para no repetir registros del mismo alumno
*** Pedir sólo el dni y usarlo como origen en consultas posteriores.
SELECT DISTINCT
dni
FROM
CursosRealizados
WHERE CursosRealizados.CombAluCur NOT IN (SELECT CombAluCur FROM Universo)
INTO CURSOR AluTodosCur


Y si todo funciona bien, esta consulta final te devolvería el listado que necesitás:
SELECT
Alumnos.Apellido,
Alumnos.Nombre
FROM
Alumnos,
AluTodosCur
WHERE
Alumnos.dni=AluTodosCur.dni

¿¿¿ Andará ??? Suerte!
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar
sin imagen de perfil

Consulta SQL; Listar TODOS los alumnos que cursen determinado curso.

Publicado por Carlos Cuyé (11 intervenciones) el 07/11/2011 23:33:05
Luego de enviar mi respuesta, vi la respuesta de Leonardo. Básicamente es la misma idea, mi conjunto Universo es el Producto Cartesiano que él mencionó.
Analizando las 2 respuestas podrás entender aún mejor la solución.
Fijate en la forma que Leonardo definió el producto cartesiano, ya que quizás en tu motor sea obligatorio el uso de la cláusula WHERE, que yo no la utilizo en la primer consulta. Yo uso mucho SQL pero en Visual FoxPro, por ello deberías adecuar la sintaxis al ejemplo de Leonardo, por ser de MySQL.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar