2009-12-23 188 views
15

Estoy buscando una forma de manejar el siguiente escenario. Tengo una tabla de base de datos que necesito devolver solo un registro para cada "identificación de grupo" que está contenida dentro de la tabla, además el registro que se selecciona dentro de cada grupo debe ser la persona más vieja en el hogar.Consulta SQL para devolver solo 1 registro por ID de grupo

ID Group ID Name    Age 
1 134  John Bowers  37 
2 134  Kerri Bowers  33 
3 135  John Bowers  44 
4 135  Shannon Bowers  42 

Así que en los datos de las muestras proporcionadas anteriormente que iba a necesitar ID 1 y 3 devueltos, ya que son las personas más ancianas dentro de cada ID de grupo.

Esto se está consultando en una base de datos de SQL Server 2005.

+0

Si desea el nombre, todavía tiene la posibilidad de elegir> 1 fila si tiene> 1 persona con la edad más avanzada. También debe establecer criterios sobre qué nombre elegir en ese caso. –

+0

Buen punto Chris. Estaba tratando de simplificar la pregunta un poco, pero eso deja agujeros como estos :-) En realidad tengo otro campo para el género, así que estoy buscando seleccionar al hombre más viejo dentro de un hogar. si hay un macho, entonces la hembra más vieja. En el caso de que haya dos hombres en el mismo hogar con la misma edad, entonces debo seleccionar solo 1 de los registros. Esto podría basarse en algo tan simple como la persona con el número de ID más bajo para el desempate. –

+1

Hay una buena discusión sobre este tipo de problema en la sección 21.4, "Funciones de Extrema", del excelente libro de Joe Celko "SQL for Smarties". Si va a encontrar algo más complicado que simples SELECT e INSERT, recomiendo este libro. – shoover

Respuesta

21
SELECT t.* 
FROM (
     SELECT DISTINCT groupid 
     FROM mytable 
     ) mo 
CROSS APPLY 
     (
     SELECT TOP 1 * 
     FROM mytable mi 
     WHERE mi.groupid = mo.groupid 
     ORDER BY 
       age DESC 
     ) t 

o esto:

SELECT * 
FROM (
     SELECT *, ROW_NUMBER() OVER (PARTITION BY groupid ORDER BY age DESC) rn 
     FROM mytable 
     ) 
WHERE rn = 1 

Esto devolverá como máximo un registro por grupo, incluso en caso de empate.

Lee este artículo en mi blog para las comparaciones de rendimiento de ambos métodos:

+0

+1: Sí, olvidé mi aviso sobre los vínculos. Demasiado ocupado luchando contra incendios. –

+0

Gracias Quassnoi. ¡Pude agregar la columna de género además de la columna de edad en tu cláusula ORDER By y obtener los resultados que estaba buscando! (La columna de género solo se discutió en un comentario después de mi pregunta inicial) ¡Su solución es perfecta y adaptable! –

+0

Pregunta de seguimiento. Estos se ejecutarán contra 175 millones de registros. ¿Es una u otra consulta más eficiente? –

0
SELECT GroupID, Name, Age 
FROM table 
INNER JOIN 
(
SELECT GroupID, MAX(Age) AS OLDEST 
FROM table 
) AS OLDESTPEOPLE 
ON 
table.GroupID = OLDESTPEOPLE.GroupID 
AND 
table.Age = OLDESTPEOPLE.OLDEST 
3

Uso:

SELECT DISTINCT 
     t.groupid, 
     t.name 
    FROM TABLE t 
    JOIN (SELECT t.groupid, 
       MAX(t.age) 'max_age' 
      FROM TABLE t 
     GROUP BY t.groupid) x ON x.groupid = t.groupid 
          AND x.max_age = t.age 

Entonces, ¿qué si hay 2+ personas con la misma edad para un grupo? Sería mejor almacenar la fecha de nacimiento en lugar de la edad: siempre se puede calcular la fecha de nacimiento para la presentación.

+0

¡Cuidado con los lazos! – Quassnoi

0

Prueba esto (Grupo asumiendo es sinónimo de hogar)

Select * From Table t 
Where Age = (Select Max(Age) 
      From Table 
      Where GroupId = t.GroupId) 

Si hay dos o más personas "más antiguas" en algunos de los hogares (Todos ellos tienen la misma edad y no hay nadie más más antiguo), entonces esto devolverá todos, no solo uno al azar.

Si esto es un problema, entonces necesita agregar otra subconsulta para devolver un valor de clave arbitraria para una persona en ese conjunto.

Select * From Table t 
Where Id = 
    (Select Max(Id) Fom Table 
    Where GroupId = t.GroupId 
     And Age = 
     (Select(Max(Age) From Table 
      Where GroupId = t.GroupId)) 
Cuestiones relacionadas