Incentrato solo su quella particolare query e con dati di esempio caricati di seguito. Questo risolve alcune altre query come count(distinct ...)
menzionato da altri.
L'alias in the HAVING
sembra sovraperformare leggermente o piuttosto sovraperformare la sua alternativa (a seconda della query).
Questo utilizza una tabella preesistente con circa 5 milioni di righe creata rapidamente tramite questa answer dei miei che impiegano dai 3 ai 5 minuti.
Struttura risultante:
CREATE TABLE `ratings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`thing` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5046214 DEFAULT CHARSET=utf8;
Ma usando invece INNODB. Crea l'anomalia del gap INNODB prevista a causa degli inserimenti di prenotazione dell'intervallo. Sto solo dicendo, ma non fa differenza. 4,7 milioni di righe.
Modifica la tabella per avvicinarti allo schema ipotizzato da Tim.
rename table ratings to students; -- not exactly instanteous (a COPY)
alter table students add column camId int; -- get it near Tim's schema
-- don't add the `camId` index yet
Quanto segue richiederà del tempo. Eseguilo ancora e ancora in blocchi, altrimenti la tua connessione potrebbe scadere. Il timeout è dovuto a 5 milioni di righe senza una clausola LIMIT nell'istruzione di aggiornamento. Nota, noi lo facciamo avere una clausola LIMIT.
Quindi lo stiamo facendo in mezzo milione di iterazioni di riga. Imposta una colonna su un numero casuale compreso tra 1 e 20
update students set camId=floor(rand()*20+1) where camId is null limit 500000; -- well that took a while (no surprise)
Continua a eseguire quanto sopra fino a quando non camId
è nullo.
L'ho eseguito tipo 10 volte (l'intera operazione richiede dai 7 ai 10 minuti)
select camId,count(*) from students
group by camId order by 1 ;
1 235641
2 236060
3 236249
4 235736
5 236333
6 235540
7 235870
8 236815
9 235950
10 235594
11 236504
12 236483
13 235656
14 236264
15 236050
16 236176
17 236097
18 235239
19 235556
20 234779
select count(*) from students;
-- 4.7 Million rows
Crea un utile indice (dopo gli inserti ovviamente).
create index `ix_stu_cam` on students(camId); -- takes 45 seconds
ANALYZE TABLE students; -- update the stats: http://dev.mysql.com/doc/refman/5.7/en/analyze-table.html
-- the above is fine, takes 1 second
Crea la tabella del campus.
create table campus
( camID int auto_increment primary key,
camName varchar(100) not null
);
insert campus(camName) values
('one'),('2'),('3'),('4'),('5'),
('6'),('7'),('8'),('9'),('ten'),
('etc'),('etc'),('etc'),('etc'),('etc'),
('etc'),('etc'),('etc'),('etc'),('twenty');
-- ok 20 of them
Esegui le due query:
SELECT students.camID, campus.camName, COUNT(students.id) as studentCount
FROM students
JOIN campus
ON campus.camID = students.camID
GROUP BY students.camID, campus.camName
HAVING COUNT(students.id) > 3
ORDER BY studentCount;
-- run it many many times, back to back, 5.50 seconds, 20 rows of output
e
SELECT students.camID, campus.camName, COUNT(students.id) as studentCount
FROM students
JOIN campus
ON campus.camID = students.camID
GROUP BY students.camID, campus.camName
HAVING studentCount > 3
ORDER BY studentCount;
-- run it many many times, back to back, 5.50 seconds, 20 rows of output
Quindi i tempi sono identici. Eseguito ciascuno una dozzina di volte.
Il EXPLAIN
l'output è lo stesso per entrambi
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+
| 1 | SIMPLE | campus | ALL | PRIMARY | NULL | NULL | NULL | 20 | Using temporary; Using filesort |
| 1 | SIMPLE | students | ref | ix_stu_cam | ix_stu_cam | 5 | bigtest.campus.camID | 123766 | Using index |
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+
Utilizzando la funzione AVG(), ottengo un aumento delle prestazioni di circa il 12% con l'alias in having
(con EXPLAIN
identico
output) dalle due query seguenti.
SELECT students.camID, campus.camName, avg(students.id) as studentAvg
FROM students
JOIN campus
ON campus.camID = students.camID
GROUP BY students.camID, campus.camName
HAVING avg(students.id) > 2200000
ORDER BY students.camID;
-- avg time 7.5
explain
SELECT students.camID, campus.camName, avg(students.id) as studentAvg
FROM students
JOIN campus
ON campus.camID = students.camID
GROUP BY students.camID, campus.camName
HAVING studentAvg > 2200000
ORDER BY students.camID;
-- avg time 6.5
E infine, il DISTINCT
:
SELECT students.camID, count(distinct students.id) as studentDistinct
FROM students
JOIN campus
ON campus.camID = students.camID
GROUP BY students.camID
HAVING count(distinct students.id) > 1000000
ORDER BY students.camID; -- 10.6 10.84 12.1 11.49 10.1 9.97 10.27 11.53 9.84 9.98
-- 9.9
SELECT students.camID, count(distinct students.id) as studentDistinct
FROM students
JOIN campus
ON campus.camID = students.camID
GROUP BY students.camID
HAVING studentDistinct > 1000000
ORDER BY students.camID; -- 6.81 6.55 6.75 6.31 7.11 6.36 6.55
-- 6.45
L'alias nell'avere è costantemente 35% più veloce con lo stesso EXPLAIN
produzione. Visto sotto. Quindi è stato dimostrato due volte che lo stesso output di Spiega non si traduce nella stessa performance, ma come indizio generale.
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+
| 1 | SIMPLE | campus | index | PRIMARY | PRIMARY | 4 | NULL | 20 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | students | ref | ix_stu_cam | ix_stu_cam | 5 | bigtest.campus.camID | 123766 | Using index |
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+
L'ottimizzatore sembra favorire l'alias in questo momento, specialmente per il DISTINCT.