seleccione todas las filas con un valor mínimo

9

En Sqlite 3, estoy tratando de descubrir cómo seleccionar filas en función de un valor mínimo. Creo que estoy limitado por no saber lo suficiente de la terminología relacionada para buscar en Google de manera efectiva.

La mesa se ve así:

num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           a           3         
1           b           4         

Quiero obtener las filas donde num2está 1, 2, y 4. Quiero hacer la selección basada en el valor mínimo de num para cada valor único de la columna de texto.

Entonces, para text = 'a', el valor mínimo de numes 0, entonces quiero las filas 1 y 2. Para text = 'b', el valor mínimo de numes 1, entonces quiero la fila 4.

Usando varias combinaciones de group by, puedo obtener filas 1y / 2o filas 1y 4. Siento que me falta un componente SQL que haga lo que quiero, pero no he podido averiguar qué podría ser.

¿Cuál es la forma correcta de hacer este tipo de consulta?

Solución posible

He encontrado una manera de hacer esto. No tengo la reputación suficiente para responder mi propia pregunta, así que estoy haciendo la actualización aquí. No estoy seguro si siempre es correcto o cómo es la eficiencia. Cualquier comentario es bienvenido.

Usé una declaración de selección compuesta, donde una consulta encuentra el valor mínimo de num para cada valor único de texto:

sqlite> select num, text from t group by text having num = min( num );
num         text      
----------  ----------
0           a         
1           b         

Luego me uní a esto con la tabla completa para obtener todas las filas que coinciden con estas dos columnas.

sqlite> with u as
      ( select num, text from t group by text having num = min( num ) )
        select t.* from t join u on t.num = u.num and t.text = u.text;
num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           b           4         
usuario35292
fuente

Respuestas:

10

Como ha visto, un simple GROUP BY no funcionará porque devolvería solo un registro por grupo.

Tu unión funciona bien. Para una tabla grande, será eficiente solo si hay un índice en las columnas de unión ( numy text).

Alternativamente, podría usar una subconsulta correlacionada:

SELECT *
FROM t
WHERE num = (SELECT MIN(num)
             FROM t AS t2
             WHERE t2.text = t.text);

SQLFiddle

Cuando se ejecuta, esta consulta no requiere una tabla temporal (su consulta requiere el resultado de u), pero ejecutará la subconsulta para cada registro t, por lo que textdebe indexarse. (O utilice un índice en ambos texty numpara obtener un índice de cobertura ).

CL.
fuente
no tiene ninguna tabla temporal en su consulta, solo un CTE, que es bastante diferente.
ypercubeᵀᴹ
Cuando se ejecuta, el resultado de la uconsulta se almacena en una tabla temporal, independientemente de si está escrito como un CTE, una vista o en línea como una subconsulta.
CL.
Gracias, esta versión es mucho más fácil de escribir que la que encontré. Conocer la terminología correcta también es útil para mí para investigar esto más.
usuario35292
@CL ¿Es así como SQLite ejecuta consultas con CTE? Tienes una referencia para eso? Porque otros DBMS no necesariamente usan tablas temporales para ctes.
ypercubeᵀᴹ
@ypercube Los CTE, las vistas y las subconsultas se aplanan o implementan como corutinas, si es posible. Pero un GROUP BY en una columna no indexada debe poder recopilar los datos de todos los grupos en paralelo, por lo que requiere alguna forma de tabla temporal (en todas las bases de datos).
CL.
1

Tiendo a hacer este tipo de cosas con una autounión externa:

SELECT
    M1.Num,
    M1.Text,
    M1.Num2
FROM
    MyDb M1
LEFT OUTER JOIN
    MyDB M2
ON
    M1.text = M2.text
AND
    M1.num > m2.num
WHERE
    M2.num is null

Esto es básicamente decir; dame todos los registros que no tengan un valor más alto, es decir, nulo.

BESO
fuente
1

Entonces, ¿cómo puede encontrar la respuesta a su pregunta la próxima vez? En mi opinión, es descomponiendo y siguiendo la lógica. Y lo entendiste bien:

Quiero hacer la selección basada en el valor mínimo de num para cada valor único de la columna de texto

Esto se traduce en:

select text, min(num) from t group by text;

(Esto debería ser equivalente a su havingconsulta. Puede ser interesante echar un vistazo a las filas donde numes igual a NULL. Para ser más precisos: eche un vistazo a qué efecto tienen las filas con valores nulos, que es posible que desee filtrar primero con a where num is not null)

Desde aquí puede lograr el resultado deseado:

select * from t where (num, text) in ( *insert query above* )

O usando una unión:

select t1.* from t t1,
    (select text, min(num) as n from t group by text) t2
where t1.num = t2.n and t1.text = t2.text.

Y cuando el rendimiento no sea suficiente para sus tablas, comience a buscar declaraciones más complejas.

Grimaldi
fuente
-2

¿No debería ser esta consulta exactamente lo que necesita?

select min(num), text, num2 group by text, num2
Jens W.
fuente
Esto devolverá los cuatro registros, porque los num2valores son únicos.
CL.