2012-01-18 15 views
5

Suponga que tiene una consulta como esta ...¿Cómo le digo a MySQL Optimizer que use el índice en una tabla derivada?

SELECT T.TaskID, T.TaskName, TAU.AssignedUsers 
FROM `tasks` T 
    LEFT OUTER JOIN (
     SELECT TaskID, GROUP_CONCAT(U.FirstName, ' ', 
      U.LastName SEPARATOR ', ') AS AssignedUsers 
     FROM `tasks_assigned_users` TAU 
      INNER JOIN `users` U ON (TAU.UserID=U.UserID) 
     GROUP BY TaskID 
    ) TAU ON (T.TaskID=TAU.TaskID) 

Varias personas pueden ser asignados a una tarea determinada. El propósito de esta consulta es mostrar una fila por cada tarea, pero con las personas asignadas a la tarea de una sola columna

Ahora ... suponga que tiene la configuración de índices adecuados en tasks, users, y tasks_assigned_users. El Optimizador de MySQL aún no usará el índice TaskID cuando se una al tasks en la tabla derivada. ¿WTF?!?!?

Entonces, mi pregunta es ... ¿cómo se puede hacer que esta consulta use el índice en tasks_assigned_users.TaskID? Las tablas temporales son cojas, así que si esa es la única solución ... el MySQL Optimizer es estúpido.

índices utilizados:

  • tareas
    • PRIMARIA - TaskID
  • usuarios
    • PRIMARIA - ID de usuario
  • tasks_assigned_users
    • PRIMARIA - (TaskID, UserID)
    • adicional de índices UNIQUE - (UserID, TaskID)

EDIT: También, this page dice que las tablas derivadas se ejecutan/materializado antes se une a ocurrir . ¿Por qué no volver a utilizar las teclas para realizar la unión?

EDIT 2: MySQL Optimizer no le permitirá poner en index hints tablas derivadas (presumiblemente porque no hay índices en las tablas derivadas)

Datos 3: Aquí es un muy buen post sobre este : http://venublog.com/2010/03/06/how-to-improve-subqueries-derived-tables-performance/ Observe que el Caso # 2 es la solución que estoy buscando, pero parece que MySQL no es compatible con esto en este momento. :(

EDITAR 4: Sólo encontró this: "A partir de MySQL 5.6.3, el optimizador maneja de manera más eficiente subconsultas en la cláusula FROM (es decir, las tablas derivadas): ... Durante la ejecución de consulta, el optimizador puede agregar un índice a una tabla derivada para acelerar la recuperación de filas. "Parece prometedor ...

+0

¿Puede también agregar los índices que está utilizando? Supongo que tiene un PK en tareas y un índice no único en tasks_assigned_users. – Luis

+0

@Luis: editó la pregunta :) – BMiner

+0

Tiene ID de tarea GROUP BY, lo que implica que varias personas podrían estar trabajando en una tarea determinada, lo que también implica cierta agregación. ¿Desea que todas las personas asignadas a una tarea determinada se enumeren en una sola columna de retorno asociada a la tarea? O bien, ¿realmente desea ver a todos asignados a una tarea, y esas tareas sin asignar, en blanco? Tal vez incluso empujar cualquier tarea SIN ASIGNACIÓN a la parte superior (o inferior) de la lista ... – DRapp

Respuesta

4

Hay una solución a esto en MySQL Server 5.6 - la versión preliminar (en el momento de escribir estas líneas) .

http://dev.mysql.com/doc/refman/5.6/en/from-clause-subquery-optimization.html

Aunque, no estoy seguro de si el optimizador de MySQL será volver a utilizar índices que ya existen cuando se "agrega índices de la tabla derivada"

Considere la siguiente consulta:

SELECCIONAR * FROM t1 JOIN (SELECCIONAR * FROM t2) AS derived_t2 ON t1.f1 = derived_t2.f1;

La documentación dice: "El optimizador construye un índice sobre la columna f1 desde derived_t2 si al hacerlo permitiría el uso del acceso de referencia para el plan de ejecución de menor costo".

OK, eso es genial, pero ¿el optimizador reutiliza los índices de t2? En otras palabras, ¿qué pasaría si existiera un índice para t2.f1? ¿Se vuelve a utilizar este índice o el optimizador vuelve a crear este índice para la tabla derivada? ¿Quién sabe?

EDIT: La mejor solución hasta MySQL 5.6 es crear una tabla temporal, crear un índice en esa tabla, y luego ejecutar la consulta SELECT en la tabla temporal.

+1

la misma situación estúpida en MariaDB 10 (años después): aunque agrupar por en derivada acelera la consulta (selección ridícula v1 del grupo t por v1) debido a la clasificación explícita, el mejor resultado es si crea todas las tablas derivadas antes la consulta principal y explícitamente agregue los índices necesarios. optimizador sux – Tertium

1

Me temo que es not possible. Tiene que crear una tabla temporal o una vista para usar un índice.

+0

Esa publicación es de 2006. ¿No ha habido cambios desde entonces? – BMiner

+0

No lo creo, por dos razones. 1) Es MySQL. 2) Hay otras publicaciones sobre este problema desde 2010, por ejemplo (http://planet.mysql.com/entry/?id=23769). Por cierto, ¿podrías usar una vista? – AndreKR

+0

No sé ... He leído que las vistas tienen problemas similares, pero lo intentaré ahora mismo ... – BMiner

2

El problema que veo es que al hacer una subconsulta no hay una tabla indexada subyacente. Si usted está teniendo una actuación que haría la agrupación al final, algo como esto:

SELECT T.TaskID, T.TaskName, GROUP_CONCAT(U.FirstName, ' ', U.LastName SEPARATOR ', ') AS AssignedUsers 
FROM `tasks` T 
    LEFT OUTER JOIN `tasks_assigned_users` TAU ON (T.TaskID=TAU.TaskID) 
    INNER JOIN `users` U ON (TAU.UserID=U.UserID) 
GROUP BY T.TaskID, T.TaskName 
+0

Esto funciona ... pero ya que es exactamente el mismo conjunto de resultados, no sé por qué MySQL no puede hacer esta optimización para mí. Además, mi consulta * real * tiene alrededor de 20 columnas; ¿Tendría que decirle a MySQL que los agrupe a todos? Yo * realmente * solo quiero agrupar el TaskID, no TaskID y TaskName ... MySQL hace un trabajo extra cuando agrupa por ambas columnas. Ya sabes lo que quiero decir? – BMiner

+0

Intenta decir y no decir; si se necesitan, es un error no declararlos para que te des cuenta fácilmente (no sé si son necesarios).No sé por qué no puede hacer la optimización; Supongo que las subconsultas son una caja negra para la consulta externa, pero no sé. Normalmente evito las subconsultas debido a problemas de rendimiento como este. – Luis

+0

¿Es MySQL lo suficientemente inteligente como para saber que GROUP BY T.TaskID, T.TaskName es lo mismo que GROUP BY T.TaskID porque T.TaskID es la clave PRIMARY? – BMiner

Cuestiones relacionadas