Tengo dos tablas en una base de datos MySQL 5.7.22: posts
y reasons
. Cada fila de publicación tiene y pertenece a muchas filas de razones. Cada razón tiene un peso asociado y, por lo tanto, cada publicación tiene un peso agregado total asociado.
Para cada incremento de 10 puntos de peso (es decir, para 0, 10, 20, 30, etc.), quiero obtener un recuento de publicaciones que tengan un peso total menor o igual a ese incremento. Esperaría que los resultados se vean así:
weight | post_count
--------+------------
0 | 0
10 | 5
20 | 12
30 | 18
... | ...
280 | 20918
290 | 21102
... | ...
1250 | 118005
1260 | 118039
1270 | 118040
Los pesos totales se distribuyen aproximadamente normalmente, con unos valores muy bajos y unos valores muy altos (el máximo es actualmente 1277), pero la mayoría en el medio. Hay poco menos de 120,000 filas posts
y alrededor de 120 pulgadas reasons
. Cada publicación tiene en promedio 5 o 6 razones.
Las partes relevantes de las tablas se ven así:
CREATE TABLE `posts` (
id BIGINT PRIMARY KEY
);
CREATE TABLE `reasons` (
id BIGINT PRIMARY KEY,
weight INT(11) NOT NULL
);
CREATE TABLE `posts_reasons` (
post_id BIGINT NOT NULL,
reason_id BIGINT NOT NULL,
CONSTRAINT fk_posts_reasons_posts (post_id) REFERENCES posts(id),
CONSTRAINT fk_posts_reasons_reasons (reason_id) REFERENCES reasons(id)
);
Hasta ahora, he intentado colocar el ID de la publicación y el peso total en una vista, luego unir esa vista para obtener un recuento agregado:
CREATE VIEW `post_weights` AS (
SELECT
posts.id,
SUM(reasons.weight) AS reason_weight
FROM posts
INNER JOIN posts_reasons ON posts.id = posts_reasons.post_id
INNER JOIN reasons ON posts_reasons.reason_id = reasons.id
GROUP BY posts.id
);
SELECT
FLOOR(p1.reason_weight / 10) AS weight,
COUNT(DISTINCT p2.id) AS cumulative
FROM post_weights AS p1
INNER JOIN post_weights AS p2 ON FLOOR(p2.reason_weight / 10) <= FLOOR(p1.reason_weight / 10)
GROUP BY FLOOR(p1.reason_weight / 10)
ORDER BY FLOOR(p1.reason_weight / 10) ASC;
Sin embargo, eso es inusualmente lento: lo dejé correr durante 15 minutos sin terminar, lo que no puedo hacer en producción.
¿Hay una manera más eficiente de hacer esto?
En caso de que esté interesado en probar todo el conjunto de datos, se puede descargar aquí . El archivo tiene alrededor de 60 MB, se expande a alrededor de 250 MB. Alternativamente, hay 12,000 filas en una esencia de GitHub aquí .
w.weight
, ¿es así? Estoy buscando contar publicaciones con un peso total (suma de pesos de sus filas de razón asociadas) de ltew.weight
.post_weights
vista existente que ya creé en lugar dereasons
.En MySQL, las variables pueden usarse en consultas tanto para calcular a partir de valores en columnas como para usar en la expresión de columnas nuevas calculadas. En este caso, el uso de una variable da como resultado una consulta eficiente:
La
d
tabla derivada es en realidad supost_weights
punto de vista. Por lo tanto, si planea mantener la vista, puede usarla en lugar de la tabla derivada:Una demostración de esta solución, que utiliza una edición concisa de la versión reducida de su configuración, se puede encontrar y jugar en SQL Fiddle .
fuente
ERROR 1055 (42000): 'd.reason_weight' isn't in GROUP BY
siONLY_FULL_GROUP_BY
está en @@ sql_mode. Inhabilitándolo, noté que su consulta es más lenta que la mía la primera vez que se ejecuta (~ 11 segundos). Una vez que los datos se almacenan en caché, es más rápido (~ 1 segundo). Mi consulta se ejecuta en aproximadamente 4 segundos cada vez.GROUP BY FLOOR(reason_weight / 10)
pero aceptaGROUP BY reason_weight
. En cuanto al rendimiento, ciertamente tampoco soy un experto cuando se trata de MySQL, fue solo una observación en mi máquina de mierda. Como ejecuté mi consulta primero, todos los datos ya deberían haberse almacenado en caché, por lo que no sé por qué fue más lento la primera vez que se ejecutó.