2010-10-14 30 views
5

Descargo de responsabilidad: Soy un SQL newb y esto es para una clase, pero realmente podría usar un poke en la dirección correcta.Limitaciones de GROUP BY

Tengo estas tres tablas:

student(_sid_, sname, sex, age, year, gpa)
section(_dname_, _cno_, _sectno_, pname)
enroll(_sid_, grade, _dname_, _cno_, _sectno_)
(claves primarias denotados por guiones)

que estoy tratando de escribir una consulta SQL compatible con Oracle que devuelve una tabla con el nombre del estudiante (student.sname) que tiene el gpa más alto en cada sección (eso incluye section.cno y section.sectno), así como todos los demás atributos de section.

he logrado utilizar una consulta agregada y GROUP BY para obtener el máximo promedio para cada sección:

SELECT MAX(s.gpa), e.cno, e.sectno 
    FROM enroll e, 
     student s 
    WHERE s.sid = e.sid 
GROUP BY e.cno, e.sectno 

Por no hablar de los otros atributos section, ni siquiera puedo encontrar la manera de virar en el nombre del estudiante (student.sname). Si lo agrego a la cláusula SELECT, tiene que incluirse en GROUP BY, lo que arruina el resto de la consulta. Si utilizo esta consulta completa dentro de la cláusula WHERE o FROM de una consulta externa, solo puedo acceder a los tres campos de la tabla, lo cual no es de mucha utilidad.

Sé que no puede darme la respuesta exacta, pero cualquier sugerencia sería apreciada.

+4

@OMG Ponies: me gusta la forma en que formateas las consultas. – JoshD

+0

@JoshD: todos lo hacemos ;-) – zerkms

+0

Lo has dicho básicamente. Agregue la tabla de sección a FROM. Agregue restricciones a la sección DÓNDE unirse para inscribirse. Agregue las columnas que desea que sean visibles a SELECT y repítalas en GROUP BY. – kevpie

Respuesta

3

Suponiendo Oracle 9i +, para obtener sólo uno de los estudiantes con el promedio más alto (en caso de empate) usan:

WITH summary AS (
    SELECT e.*, 
      s.name, 
      ROW_NUMBER() OVER(PARTITION BY e.cno, e.sectno 
           ORDER BY s.gpa DESC) AS rank 
    FROM ENROLL e 
    JOIN STUDENT s ON s.sid = e.sid) 
SELECT s.* 
    FROM summary s 
WHERE s.rank = 1 

no CTE equivalente:

SELECT s.* 
    FROM (SELECT e.*, 
       s.name, 
       ROW_NUMBER() OVER(PARTITION BY e.cno, e.sectno 
            ORDER BY s.gpa DESC) AS rank 
      FROM ENROLL e 
      JOIN STUDENT s ON s.sid = e.sid) s 
WHERE s.rank = 1 

Si desea ver todos los estudiantes que empataron para GPA, use:

WITH summary AS (
    SELECT e.*, 
      s.name, 
      DENSE_RANK OVER(PARTITION BY e.cno, e.sectno 
           ORDER BY s.gpa DESC) AS rank 
    FROM ENROLL e 
    JOIN STUDENT s ON s.sid = e.sid) 
SELECT s.* 
    FROM summary s 
WHERE s.rank = 1 
1

Sugerencia: considere que podría haber más de un estudiante con el promedio de calificaciones más alto de una clase. Una consulta externa solo necesita los tres campos.

0

Tal vez más corto:

SELECT DISTINCT e.cno, e.sectno , e..., 
     FIRST_VALUE(s.sname) OVER 
           (PARTITION BY e.cno, e.sectno ORDER BY s.gpa DESC) 
FROM enroll e, 
    student s 
WHERE s.sid = e.sid 

O

SELECT A.* 
FROM 
    ( SELECT s._sid, s.sname, e.cno, e.sectno ,..., s.gpa 
       MAX(s.gpa) OVER (PARTITION BY e.cno, e.sectno) AS maxgpa 
     FROM enroll e, 
      student s 
     WHERE s.sid = e.sid 
    ) A 
WHERE A.maxgpa = A.gpa 
0

Éstos son algunos consejos: -

  1. Está en el camino correcto con su consulta GROUP BY
  2. Esto devuelve usted el GPA máximo para cada sección basada en los campos cno y sectno
  3. Ahora, tiene el valor de GPA máximo para cada combinación de cno y sectno.
  4. Utilice esta información que tiene de manera inversa si desea ahora encontrar todos los estudiantes que coincidan con estos valores de combinación. SUGERENCIA: Considere los resultados de la consulta GROUP BY como una tabla y utilizar un INNER JOIN
  5. incluso si es posible que haya más de 1 estudiantes para el mismo máximo GPA, usted seguirá recibiendo todos ellos

Espero que esto ayude !!

0

Esto debería darle lo que está buscando. Consulte la función RANK() de Oracle para obtener detalles sobre cómo los GPA se clasifican de mayor a menor por sección.

Requisitos:

devolver una tabla con el nombre del estudiante (student.sname) que tiene el promedio más alto en cada sección (section.cno y section.sectno), así como todos los demás atributos de la sección .

SELECT * FROM 
(
    SELECT 
     s.sname, 
     s.gpa, 
     sec.dname, 
     sec.cno, 
     sec.sectno, 
     sec.pname, 
     /* for each "sec.cno, sec.sectno", this will rank each GPA in order from highest to lowest. Ties will have the same rank. */ 
     RANK() OVER(PARTITION BY sec.cno, sec.sectno ORDER BY s.gpa DESC) as r_rank 
    FROM 
     enroll e, 
     student s, 
     section sec 
    WHERE 
     /* join enroll with student */ 
     s.sid = e.sid 
     /* join section with enroll */ 
     AND sec.dname = e.dname 
     AND sec.cno = e.cno 
     AND sec.sectno = e.sectno 
) 
WHERE r_rank = 1 /* this returns only the highest GPA (maybe multiple students) for each "sec.cno, sec.sectno" combination */ 
; 

Nota: Si no desea que los lazos, el cambio RANK() para ROW_NUMBER()