Skoncentrowany wąsko tylko na tym konkretnym zapytaniu, z przykładowymi danymi załadowanymi poniżej. To odnosi się do niektórych innych zapytań, takich jak count(distinct ...)
wspomniane przez innych.
Alias alias in the HAVING
wydaje się być albo nieco lepszy, albo całkiem lepszy od swojej alternatywy (w zależności od zapytania).
Wykorzystuje to istniejącą wcześniej tabelę z około 5 milionami wierszy utworzoną szybko za pomocą tej odpowiedzi mojego, co zajmuje od 3 do 5 minut.
Wynikowa struktura:
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;
Ale zamiast tego używam INNODB. Tworzy oczekiwaną anomalię przerwy INNODB ze względu na wstawienie rezerwacji zakresu. Tylko mówię, ale nie ma to znaczenia. 4,7 miliona rzędów.
Zmodyfikuj tabelę, aby zbliżyć się do założonego schematu Tima.
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
To zajmie trochę czasu. Uruchamiaj go raz za razem w kawałkach, w przeciwnym razie połączenie może wygasnąć. Limit czasu wynika z 5 milionów wierszy bez klauzuli LIMIT w instrukcji aktualizacji. Uwaga, tak mieć klauzulę LIMIT.
Więc robimy to w iteracjach pół miliona wierszy. Ustawia kolumnę na liczbę losową od 1 do 20
update students set camId=floor(rand()*20+1) where camId is null limit 500000; -- well that took a while (no surprise)
Kontynuuj powyższe, aż nie będzie camId
jest zerowe.
Uruchomiłem to jak 10 razy (całość trwa od 7 do 10 minut)
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
Utwórz przydatny indeks (oczywiście po wstawkach).
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
Utwórz tabelę kampusową.
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
Uruchom dwa zapytania:
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
i
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
Więc czasy są identyczne. Uruchomiono każdy kilkanaście razy.
EXPLAIN
dane wyjściowe są takie same dla obu
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+
| 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 |
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+
Używając funkcji AVG(), uzyskuję około 12% wzrost wydajności dzięki aliasowi w having
(z identycznym EXPLAIN
wyjście) z następujących dwóch zapytań.
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
I wreszcie, 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
Alias w posiadaniu stale działa 35% szybciej z tym samym EXPLAIN
wyjście. Widziane poniżej. Tak więc te same dane wyjściowe wyjaśniania zostały pokazane dwukrotnie, aby nie dać takiej samej wydajności, ale jako ogólna wskazówka.
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+
| 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 |
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+
Optymalizator wydaje się faworyzować alias w posiadaniu w tej chwili, szczególnie w przypadku DISTINCT.