Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Implicações de desempenho de permitir que o alias seja usado na cláusula HAVING


Focado estritamente apenas nessa consulta específica e com dados de exemplo carregados abaixo. Isso aborda algumas outras consultas, como o count(distinct ...) mencionado por outros.

O alias in the HAVING parece ter um desempenho ligeiramente superior ou um pouco superior à sua alternativa (dependendo da consulta).

Isso usa uma tabela pré-existente com cerca de 5 milhões de linhas criadas rapidamente por meio desta resposta do meu que leva de 3 a 5 minutos.

Estrutura resultante:
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;

Mas usando INNODB em vez disso. Cria a anomalia de intervalo INNODB esperada devido às inserções de reserva de intervalo. Apenas dizendo, mas não faz diferença. 4,7 milhões de linhas.

Modifique a tabela para se aproximar do esquema assumido por 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

O seguinte vai demorar um pouco. Execute-o repetidamente em partes ou então sua conexão pode expirar. O tempo limite é devido a 5 milhões de linhas sem uma cláusula LIMIT na instrução de atualização. Observe que fazemos tem uma cláusula LIMIT.

Então, estamos fazendo isso em meio milhão de iterações de linha. Define uma coluna para um número aleatório entre 1 e 20
update students set camId=floor(rand()*20+1) where camId is null limit 500000; -- well that took a while (no surprise)

Continue executando o acima até que não haja camId é nulo.

Eu executei umas 10 vezes (a coisa toda leva de 7 a 10 minutos)
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

Crie um índice útil (após as inserções, é claro).
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

Crie a tabela do 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

Execute as duas consultas:
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

Portanto, os tempos são idênticos. Correu cada uma dúzia de vezes.

O EXPLAIN saída é a mesma para ambos
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+
| 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                     |
+----+-------------+----------+------+---------------+------------+---------+----------------------+--------+---------------------------------+

Usando a função AVG(), estou obtendo um aumento de cerca de 12% no desempenho com o alias no having (com idêntico EXPLAIN output) das duas consultas a seguir.
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 por último, o 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

O alias de ter executado consistentemente 35% mais rápido com o mesmo EXPLAIN saída. Visto abaixo. Portanto, a mesma saída do Explain foi mostrada duas vezes para não resultar no mesmo desempenho, mas como uma pista geral.
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+
| 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                                  |
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+

O Otimizador parece favorecer o alias no momento, especialmente para o DISTINCT.