Úzce zaměřené pouze na tento konkrétní dotaz a s ukázkovými údaji načtenými níže. To řeší některé další dotazy, jako je count(distinct ...)
zmínění ostatními.
alias in the HAVING
zdá se, že buď mírně převyšuje nebo o něco převyšuje svou alternativu (v závislosti na dotazu).
Toto používá již existující tabulku s asi 5 miliony řádků vytvořenou rychle pomocí této odpovědi můj, který trvá 3 až 5 minut.
Výsledná 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 místo toho použijte INNODB. Vytváří očekávanou anomálii mezery INNODB kvůli vložkám rezervace rozsahu. Jen říkám, ale nezáleží na tom. 4,7 milionu řádků.
Upravte tabulku tak, aby se přiblížila Timovu předpokládanému schématu.
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
Následující bude chvíli trvat. Spusťte jej znovu a znovu po částech, jinak může dojít k vypršení časového limitu vašeho připojení. Časový limit je způsoben 5 miliony řádků bez klauzule LIMIT v prohlášení o aktualizaci. Poznámka, děláme mají klauzuli LIMIT.
Takže to děláme v půl milionu řádkových iterací. Nastaví sloupec na náhodné číslo mezi 1 a 20
update students set camId=floor(rand()*20+1) where camId is null limit 500000; -- well that took a while (no surprise)
Pokračujte ve spouštění výše, dokud nezmizí camId
je null.
Spustil jsem to asi 10krát (celá věc trvá 7 až 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
Vytvořte užitečný rejstřík (samozřejmě po vložkách).
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
Vytvořte tabulku kampusu.
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
Spusťte dva dotazy:
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
a
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
Časy jsou tedy stejné. Každý běžel tucetkrát.
EXPLAIN
výstup je stejný pro oba
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+
| 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 |
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+
Pomocí funkce AVG() získávám asi 12% nárůst výkonu s aliasem v having
(s identickým EXPLAIN
výstup) z následujících dvou dotazů.
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
A nakonec 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 v mít neustále běží o 35 % rychleji se stejným EXPLAIN
výstup. Viděno níže. Stejný výstup Explain byl tedy ukázán dvakrát, aby nevedl ke stejnému výkonu, ale jako obecné vodítko.
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+
| 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 |
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+
Zdá se, že optimalizátor upřednostňuje alias v současné době, zejména pro DISTINCT.