sql >> Databáze >  >> RDS >> Mysql

Důsledky pro výkon povolení použití aliasu v klauzuli HAVING

Ú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.



  1. Inicializace PHP PDO se nezdařila kvůli dvojitému portu - Nezachycená výjimka PDO:SQLSTATE[HY000] [2002]

  2. Optimalizace aktualizačních dotazů

  3. Jak přidat komentáře v SQL?

  4. customer.pk_name spojení transakcí.fk_name vs. customer.pk_id [sériové] spojení transakcí.fk_id [integer]