Pregunta

Dado que tengo una tabla con el siguiente contenido muy simple:

# select * from messages;
  id | verbosity 
 ----+-----------
   1 |        20
   2 |        20
   3 |        20
   4 |        30
   5 |       100
 (5 rows)

Me gustaría seleccionar N mensajes, cuya suma de detalle sea inferior a Y (para fines de prueba, digamos que debería ser 70, luego los resultados correctos serán mensajes con ID 1,2,3).Es realmente importante para mí que esa solución sea independiente de la base de datos (debería funcionar al menos en Postgres y SQLite).

Estaba intentando con algo como:

SELECT * FROM messages GROUP BY id HAVING SUM(verbosity) < 70;

Sin embargo, no parece funcionar como se esperaba, porque en realidad no suma todos los valores de la columna de detalle.

Estaría muy agradecido por cualquier sugerencia/ayuda.

¿Fue útil?

Solución

SELECT m.id, sum(m1.verbosity) AS total
FROM   messages m
JOIN   messages m1 ON m1.id <= m.id
WHERE  m.verbosity < 70    -- optional, to avoid pointless evaluation
GROUP  BY m.id
HAVING SUM(m1.verbosity) < 70
ORDER  BY total DESC
LIMIT  1;

Esto supone un único y ascendente id como lo tienes en tu ejemplo.


En el Postgres moderno -o en general con SQL estándar moderno (pero no en SQLite):

CTE simple

WITH cte AS (
   SELECT *, sum(verbosity) OVER (ORDER BY id) AS total
   FROM   messages
   )
SELECT *
FROM   cte
WHERE  total <= 70
ORDER  BY id;

CTE recursivo

Debería ser más rápido para mesas grandes donde solo se recupera un conjunto pequeño.

WITH RECURSIVE cte AS (
   (  -- parentheses required
   SELECT id, verbosity, verbosity AS total
   FROM   messages
   ORDER  BY id
   LIMIT  1
   )

   UNION ALL 
   SELECT c1.id, c1.verbosity, c.total + c1.verbosity 
   FROM   cte c
   JOIN   LATERAL (
      SELECT *
      FROM   messages
      WHERE  id > c.id
      ORDER  BY id
      LIMIT  1
      ) c1 ON  c1.verbosity <= 70 - c.total
   WHERE c.total <= 70
   )
SELECT *
FROM   cte
ORDER  BY id;

Todas las características estándar, excepto LIMIT.

Estrictamente hablando, no existe el concepto "independiente de la base de datos".Existen varios estándares SQL, pero ningún RDBMS los cumple por completo. LIMIT funciona para PostgreSQL y SQLite (y algunos otros).Usar TOP 1 para servidor SQL, rownum para Oráculo.Aquí está un lista completa en Wikipedia.

El SQL: estándar 2008 sería:

...
FETCH  FIRST 1 ROWS ONLY

...que admite PostgreSQL, pero casi ningún otro RDBMS.

La alternativa pura que funciona con más sistemas sería envolverla en una subconsulta y

SELECT max(total) FROM <subquery>

Pero eso es lento y difícil de manejar.

Violín SQL.

Otros consejos

Esto funcionará ...

select * 
from messages
where id<=
(
    select MAX(id) from
    (
        select m2.id, SUM(m1.verbosity) sv 
        from messages m1
        inner join messages m2 on m1.id <=m2.id
        group by m2.id
    ) v
    where sv<70
)

Sin embargo, debe comprender que SQL está diseñado como un lenguaje a base de conjunto, en lugar de una iterativa, por lo que está diseñado para tratar los datos como un conjunto, en lugar de una fila por fila.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top