2009-07-31 10 views
12

así que sé que esto es una pregunta bastante tonto, sin embargo (como dice el título más largamente) Me gustaría saber cómo hacer lo siguiente:¿Cómo se agrupa en una columna y recupera una fila con el valor mínimo de otra columna en T/SQL?

tengo una tabla como la siguiente:

ID Foo Bar Blagh 
---------------- 
1 10 20 30 
2 10 5 1 
3 20 50 40 
4 20 75 12 

I quiero agrupar por Foo, a continuación, extraiga filas con Bar mínima, es decir, yo quiero lo siguiente:

ID Foo Bar Blagh 
---------------- 
2 10 5 1 
3 20 50 40 

no puedo por la vida de mí averiguar la correcta SQL para recuperar esto. Quiero algo como:

SELECT ID, Foo, Bar, Blagh 
FROM Table 
GROUP BY Foo 
HAVING(MIN(Bar)) 

Sin embargo, esto claramente no funciona ya que es completamente invalidez teniendo en sintaxis y la ID, Foo, Bar y Blagh no se agregan.

¿Qué estoy haciendo mal?

Respuesta

11

This es casi exactamente la misma pregunta, pero tiene algunas respuestas!

Aquí es burlarse de mí hasta su mesa:

declare @Borg table (
    ID int, 
    Foo int, 
    Bar int, 
    Blagh int 
) 
insert into @Borg values (1,10,20,30) 
insert into @Borg values (2,10,5,1) 
insert into @Borg values (3,20,50,70) 
insert into @Borg values (4,20,75,12) 

entonces usted puede hacer una combinación interna anónima para obtener los datos que desea.

select B.* from @Borg B inner join 
(
    select Foo, 
     MIN(Bar) MinBar 
    from @Borg 
    group by Foo 
) FO 
on FO.Foo = B.Foo and FO.MinBar = B.Bar 

EDITAR Adam Robinson amablemente ha señalado que "esta solución tiene el potencial de devolver varias filas cuando se duplica el valor mínimo de la barra, y elimina cualquier valor de foo dónde está bar null"

Dependiendo de su caso de uso, los valores duplicados donde Bar está duplicado podrían ser válidos; si deseaba encontrar todos los valores en Borg donde Bar era mínimo, entonces tener ambos resultados parece ser el camino a seguir.

Si necesita capturar NULLs en el campo a través del cual se le agregan (por MIN en este caso), entonces se podría coalesce el NULL con una aceptablemente alto (o bajo) valor (esto es un hack):

... 
MIN(coalesce(Bar,1000000)) MinBar -- A suitably high value if you want to exclude this row, make it suitably low to include 
... 

O usted podría ir para una unión y adjuntar todos esos valores a la parte inferior del conjunto de resultados.

on FO.Foo = B.Foo and FO.MinBar = B.Bar 
union select * from @Borg where Bar is NULL 

este último no los valores de grupo en @Borg con el mismo valor Foo porque no saben cómo seleccionar entre ellos.

+1

+1 para unir en lugar de tirar un poco de SQL juntos tan rápido como se puede ... – gbn

+2

fresco. Editando mientras comento. – gbn

+1

@gbn ¡Estaba "agregando valor" a mi respuesta! :) – butterchicken

1

Según tengo entendido, no se puede hacer esto de una vez.

select Foo, min(Bar) from table group by Foo 

,, le ofrece la barra mínima para cada Foo distinto. Pero no puede vincular ese mínimo a una ID particular, porque podría haber más de una fila con ese valor de barra.

Lo que puede hacer es algo como esto:

select * from Table t 
join (
    select Foo, min(Bar) as minbar 
    from Table group by Foo 
) tt on t.Foo=tt.Foo and t.Bar=tt.minbar 

Tenga en cuenta que si hay es más de una fila con el valor mínimo de barra, que obtendrá a todos con la consulta anterior.

Ahora, no estoy diciendo que ser un gurú de SQL, y es tarde donde estoy, y me pueden faltar algo, pero no es mi $ 0,02 :)

1

Esto podría ayudar:

DECLARE @Table TABLE(
     ID INT, 
     Foo FLOAT, 
     Bar FLOAT, 
     Blah FLOAT 
) 

INSERT INTO @Table (ID,Foo,Bar,Blah) SELECT 1, 10 ,20 ,30 
INSERT INTO @Table (ID,Foo,Bar,Blah) SELECT 2, 10 ,5 ,1 
INSERT INTO @Table (ID,Foo,Bar,Blah) SELECT 3, 20 ,50 ,40 
INSERT INTO @Table (ID,Foo,Bar,Blah) SELECT 4, 20 ,75 ,12 

SELECT t.* 
FROM @Table t INNER JOIN 
     (
      SELECT Foo, 
        MIN(Bar) MINBar 
      FROM @Table 
      GROUP BY Foo 
     ) Mins ON t.Foo = Mins.Foo 
       AND t.Bar = Mins.MINBar 
3
select 
    ID, 
    Foo, 
    Bar, 
    Blagh 
from Table 
join (
    select 
    ID, 
    (row_number() over (order by foo, bar) - rank() over (order by foo)) as rowNumber 
) t on t.ID = Table.ID and t.rowNumber = 0 

esto se une a la mesa de nuevo, pero esta vez añade un número de fila relativa para el valor de bar, como si estuviera clasificado de forma ascendente dentro de cada valor de foo. Al filtrar en rowNumber = 0, selecciona solo los valores más bajos para bar para cada valor de foo. Esto también elimina efectivamente la cláusula group by, ya que ahora solo está recuperando una fila por foo.

+0

Vale la pena mencionar que esto no funciona antes de MSSQL2005 cuando se introdujo row_number(). – butterchicken

1

Otra opción sería algo en la línea de lo siguiente:

DECLARE @TEST TABLE( ID int, Foo int, Bar int, Blagh int) 
INSERT INTO @TEST VALUES (1,10,20,30) 
INSERT INTO @TEST VALUES (2,10,5,1) 
INSERT INTO @TEST VALUES (3,20,50,70) 
INSERT INTO @TEST VALUES (4,20,75,12) 

SELECT Id, Foo, Bar, Blagh 
FROM (
     SELECT id, Foo, Bar, Blagh, CASE WHEN (Min(Bar) OVER(PARTITION BY FOO) = Bar) THEN 1 ELSE 0 END as MyRow 
     FROM @TEST) t 
WHERE MyRow = 1 

Aunque esto todavía requiere un sub-consulta sí elimina la necesidad de uniones.

Otra opción.

0
declare @Borg table (
    ID int, 
    Foo int, 
    Bar int, 
    Blagh int 
) 
insert into @Borg values (1,10,20,30) 
insert into @Borg values (2,10,5,1) 
insert into @Borg values (3,20,50,70) 
insert into @Borg values (4,20,75,12) 

select * from @Borg 

select Foo,Bar,Blagh from @Borg b 
where Bar = (select MIN(Bar) from @Borg c where c.Foo = b.Foo) 
+0

No copie la respuesta de otra persona. –

Cuestiones relacionadas