2010-08-19 16 views
7

Tengo una consulta UNION que consiste en dos consultas rápidas.Slow UNION Query - MySQL

(SELECT DISTINCT (SELECT strStatus FROM User_User_XR uuxr WHERE 
(uuxr.intUserId1 = '1' AND uuxr.intUserId2 = u.intUserId)) AS strFriendStatus1, 
uuxro.strStatus AS strFriendStatus2, uuxr.intUserId2 AS intUserId, u.strUserName , 
u.strGender, IF(u.dtmBirth != '0000-00-00', FLOOR(DATEDIFF(CURDATE(), 
u.dtmBirth)/365.25) , '?') AS intAge, u.strCountry AS strCountryCode, 
c.strCountry AS strCountry, u.strAvatar, u.fltPoints, 
IF(o.intUserId IS NULL, 'offline', 'online') AS strOnline, 
IF (u.strAvatar != '', CONCAT('avatars/60/', u.strAvatar), 
CONCAT('images/avatar_', u.strGender, '_small.png')) as strAvatar,  
IF (u.strAvatar != '', CONCAT('avatars/150/', u.strAvatar),  
CONCAT('images/avatar_', u.strGender, '.png')) as strLargeAvatar, 
u.dtmLastLogin, u.dtmRegistered FROM User_User_XR uuxr, 
User u LEFT JOIN User_User_XR uuxro ON uuxro.intUserId2 = '1' 
AND uuxro.intUserId1 = u.intUserId 
LEFT JOIN Online o ON o.intUserId = u.intUserId 
LEFT JOIN Country c ON c.strCountryCode = u.strCountry 
WHERE u.intUserId = uuxr.intUserId2 AND (uuxr.strStatus = 'confirmed') 
AND uuxr.intUserId1='1') 

UNION 

(SELECT DISTINCT (SELECT strStatus FROM User_User_XR uuxr 
WHERE (uuxr.intUserId1 = '1' AND uuxr.intUserId2 = u.intUserId)) AS strFriendStatus1, 
uuxro.strStatus AS strFriendStatus2, uuxr.intUserId1 AS intUserId, u.strUserName , 
u.strGender, IF(u.dtmBirth != '0000-00-00', FLOOR(DATEDIFF(CURDATE(), 
u.dtmBirth)/365.25) , '?') AS intAge, 
u.strCountry AS strCountryCode, c.strCountry AS strCountry, u.strAvatar, u.fltPoints, 
IF(o.intUserId IS NULL, 'offline', 'online') AS strOnline, 
IF (u.strAvatar != '', CONCAT('avatars/60/', u.strAvatar), 
CONCAT('images/avatar_', u.strGender, '_small.png')) as strAvatar, 
IF (u.strAvatar != '', CONCAT('avatars/150/', u.strAvatar), 
CONCAT('images/avatar_', u.strGender, '.png')) as strLargeAvatar, 
u.dtmLastLogin, u.dtmRegistered FROM User_User_XR uuxr, User u 
LEFT JOIN User_User_XR uuxro ON uuxro.intUserId2 = '1' 
AND uuxro.intUserId1 = u.intUserId 
LEFT JOIN Online o ON o.intUserId = u.intUserId 
LEFT JOIN Country c ON c.strCountryCode = u.strCountry 
WHERE u.intUserId = uuxr.intUserId1 AND (uuxr.strStatus = 'confirmed') 
AND uuxr.intUserId2='1') 

La primera de las consultas se ejecuta en 0.0047s En segundo lugar se ejecuta en 0.0043s

Sin embargo, con la unión, corren 0.27s ... ¿por qué es esto? No hay Order By después de UNION, ¿por qué MySQL simplemente no tomaría las dos consultas rápidas y las concatenaría?

Respuesta

2

Intente utilizar UNION ALL.

UNION por sí solo eliminará los registros duplicados, lo que implica una clasificación detrás de escena.

+0

UNION ALL lo redujo a 0.17s .. Hizo algo, pero sigue siendo demasiado lento :(... gracias por la respuesta :) – Armin

11

A UNION hace que se cree una tabla temporal, incluso para UNION ALL.

Cuando se realiza un UNION DISTINCT (que es lo mismo que UNION), la tabla temporal se crea con un índice para que se puedan eliminar los duplicados. Con UNION ALL, se crea la tabla temporal, pero sin el índice.

Esto explica la ligera mejora del rendimiento cuando se utiliza UNION ALL, y también representa la disminución del rendimiento cuando se usa UNION en lugar de dos consultas separadas.

Para obtener más información al respecto, consulte la siguiente entrada en el MySQL performance blog:

UNION vs UNION ALL Performance

La página How MySQL Uses Internal Temporary Tables de la documentación de MySQL establece que una tabla temporal se crea cuando:

. .. cualquier columna más grande que 512 bytes en la lista SELECCIONAR, si se usa UNION o UNION ALL

+0

Gracias por la explicación :). Muy útil para el conocimiento, pero no me ayuda a resolver mi problema. Básicamente estoy tratando de optimizar todas mis consultas. Para mí, cualquier consulta que se ejecute más lento que 0.1 es un problema. Tengo un sitio grande con muchos visitantes y no puedo darme el lujo de tener una consulta como esta corriendo a 0.17s. Hasta ahora, mi mejor opción es simplemente ejecutar dos consultas y fusionar las matrices resultantes con PHP, lo cual es bastante desagradable modularidad. – Armin

+0

@Armin: estoy de acuerdo, no ayuda mucho. Noto que ambas consultas son muy similares: ¿podrían combinarse en una sola declaración 'SELECT'? Se diferencian en la condición 'WHERE ', y parece que esto podría reducirse utilizando un par de operadores' OR '. El único otro problema que veo (y solo lo he visto rápidamente) es que tu devolución 'uuxr.intUserId1 AS intUserId' y' uuxr.intUserId2 AS intUserId'. Tal vez podría usar un 'SI' para seleccionar la columna correcta para regresar de una sola declaración. – Mike

+0

Tuve esto antes. La consulta sería extremadamente lenta. Aquí hay un ejemplo. Establecí un límite de solo 5 registros aquí y se ejecuta en 0.47s. 'SELECT u.intUserId, u.strUserName FROM User_User_XR uuxr, User u WHERE (uuxr.intUserId1 = '1' AND uuxr.intUserId2 = u.intUserId) O (uuxr.intUserId2 = '1' AND uuxr.intUserId1 = u. intUserId) AND uuxr.strStatus = 'confirmado' LIMITE 5 ' – Armin