sql >> Base de Datos >  >> RDS >> Mysql

Implicaciones de rendimiento de permitir el uso de alias en la cláusula HAVING

Estrechamente enfocado solo en esa consulta en particular, y con datos de muestra cargados a continuación. Esto aborda algunas otras consultas, como count(distinct ...) mencionado por otros.

El alias in the HAVING parece superar ligeramente o bastante su alternativa (dependiendo de la consulta).

Esto usa una tabla preexistente con alrededor de 5 millones de filas creada rápidamente a través de esta respuesta el mío, que tarda de 3 a 5 minutos.

Estructura 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;

Pero usando INNODB en su lugar. Crea la anomalía de espacio INNODB esperada debido a las inserciones de reserva de rango. Solo digo, pero no hace ninguna diferencia. 4,7 millones de filas.

Modifique la tabla para acercarse al esquema asumido 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

Lo siguiente llevará un tiempo. Ejecútelo una y otra vez en fragmentos o, de lo contrario, su conexión puede agotarse. El tiempo de espera se debe a 5 millones de filas sin una cláusula LIMIT en la declaración de actualización. Tenga en cuenta que hacemos tener una cláusula LIMIT.

Así que lo estamos haciendo en medio millón de iteraciones de fila. Establece una columna en un número aleatorio entre 1 y 20

update students set camId=floor(rand()*20+1) where camId is null limit 500000; -- well that took a while (no surprise)

Continúe ejecutando lo anterior hasta que no haya camId es nulo.

Lo ejecuté como 10 veces (todo toma 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

Cree un índice útil (después de las inserciones, por supuesto).

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

Cree la tabla 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

Ejecute las dos 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

y

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

Así que los tiempos son idénticos. Corrió cada uno una docena de veces.

El EXPLAIN la salida es la misma 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 la función AVG(), obtengo un aumento del 12 % en el rendimiento con el alias en having (con idéntico EXPLAIN salida) de las siguientes dos consultas.

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

Y por último, el 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

El alias en el que tiene se ejecuta consistentemente 35% más rápido con el mismo EXPLAIN producción. Visto a continuación. Por lo tanto, la misma salida de Explicación se ha mostrado dos veces para no generar el mismo rendimiento, sino como una pista general.

+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+
| 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                                  |
+----+-------------+----------+-------+---------------+------------+---------+----------------------+--------+----------------------------------------------+

El Optimizer parece favorecer el alias en el momento, especialmente para el DISTINCT.