Saya punya dua tabel di database MySQL 5.7.22: posts
dan reasons
. Setiap baris posting memiliki dan memiliki banyak alasan. Setiap alasan memiliki bobot yang terkait dengannya, dan oleh karena itu setiap pos memiliki total bobot agregat yang terkait dengannya.
Untuk setiap kenaikan 10 poin berat (yaitu untuk 0, 10, 20, 30, dll), saya ingin mendapatkan hitungan posting yang memiliki berat total kurang dari atau sama dengan kenaikan itu. Saya berharap hasil untuk itu terlihat seperti ini:
weight | post_count
--------+------------
0 | 0
10 | 5
20 | 12
30 | 18
... | ...
280 | 20918
290 | 21102
... | ...
1250 | 118005
1260 | 118039
1270 | 118040
Berat total kira-kira terdistribusi normal, dengan beberapa nilai sangat rendah dan beberapa nilai sangat tinggi (maksimum saat ini 1277), tetapi mayoritas di tengah. Ada hanya di bawah 120.000 baris posts
, dan sekitar 120 di reasons
. Setiap posting memiliki rata-rata 5 atau 6 alasan.
Bagian-bagian tabel yang relevan terlihat seperti ini:
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)
);
Sejauh ini, saya sudah mencoba menjatuhkan ID pos dan bobot total ke tampilan, lalu menggabungkan tampilan itu ke dirinya sendiri untuk mendapatkan jumlah teragregasi:
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;
Namun, itu sangat lambat - saya membiarkannya berjalan selama 15 menit tanpa berhenti, yang tidak dapat saya lakukan dalam produksi.
Apakah ada cara yang lebih efisien untuk melakukan ini?
Jika Anda tertarik untuk menguji seluruh dataset, ini dapat diunduh di sini . File ini sekitar 60MB, diperluas menjadi sekitar 250MB. Bergantian, ada 12.000 baris dalam inti GitHub di sini .
w.weight
- apakah itu benar? Saya ingin menghitung posting dengan total bobot (jumlah bobot dari baris alasan terkait) dari ltew.weight
.post_weights
yang sudah saya buat bukanreasons
.Di MySQL, variabel dapat digunakan dalam kueri untuk dihitung dari nilai dalam kolom dan untuk digunakan dalam ekspresi untuk kolom baru yang dihitung. Dalam kasus ini, menggunakan variabel akan menghasilkan kueri yang efisien:
The
d
table berasal sebenarnya Andapost_weights
lihat. Oleh karena itu, jika Anda berencana mempertahankan tampilan, Anda dapat menggunakannya sebagai ganti tabel turunan:Demo dari solusi ini, yang menggunakan edisi singkat dari versi yang dikurangi dari pengaturan Anda, dapat ditemukan dan dimainkan di SQL Fiddle .
sumber
ERROR 1055 (42000): 'd.reason_weight' isn't in GROUP BY
apakahONLY_FULL_GROUP_BY
ada di @@ sql_mode. Menonaktifkannya Saya perhatikan bahwa permintaan Anda lebih lambat dari saya pertama kali dijalankan (~ 11 detik). Setelah data di-cache lebih cepat (~ 1 detik). Permintaan saya berjalan sekitar 4 detik setiap kali.GROUP BY FLOOR(reason_weight / 10)
tetapi menerimaGROUP BY reason_weight
. Adapun kinerja saya tentu bukan ahli juga ketika datang ke MySQL, itu hanya pengamatan pada mesin jelek saya. Karena saya menjalankan kueri saya pertama-tama semua data seharusnya sudah di-cache, jadi saya tidak tahu mengapa ini lebih lambat saat pertama kali dijalankan.