2011-11-29 19 views
7

Soy muy nuevo en MySQL y gracias al gran apoyo de ustedes muchachos más experimentados aquí estoy logrando luchar, mientras aprendí mucho en el proceso.¿Cómo puedo simplificar/mejorar el rendimiento de esta consulta MySQL?

Tengo una consulta que hace exactamente lo que quiero. Sin embargo, me parece extremadamente desordenado y estoy seguro de que debe haber una forma de simplificarlo.

¿Cómo se puede mejorar y optimizar esta consulta para el rendimiento?

Muchas gracias

  $sQuery = " 
     SELECT SQL_CALC_FOUND_ROWS ".str_replace(" , ", " ", implode(", ", $aColumns))." 

    FROM $sTable b 
    LEFT JOIN (
    SELECT COUNT(*) AS projects_count, a.songs_id 

    FROM $sTable2 a 
    GROUP BY a.songs_id 
) bb ON bb.songs_id = b.songsID 

LEFT JOIN (
    SELECT AVG(rating) AS rating, COUNT(rating) AS ratings_count, c.songid 

FROM $sTable3 c 

    GROUP BY c.songid 
) bbb ON bbb.songid = b.songsID 

LEFT JOIN (
    SELECT c.songid, c.userid, 

    CASE WHEN EXISTS 
    ( 
     SELECT songid 
     FROM $sTable3 
     WHERE songid = c.songid 
    ) Then 'User Voted' 
    else 
    (
     'Not Voted' 
    ) 
    end 
    AS voted 
FROM $sTable3 c 
WHERE c.userid = $userid 


    GROUP BY c.songid 
) bbbb ON bbbb.songid = b.songsID 

EDIT: He aquí una descripción de lo que la consulta está haciendo: -

Tengo tres tablas:

  • $ estable = una mesa de canciones (songid, mp3link, artwork, useruploadid etc.)

  • $ = sTable2 una mesa de proyectos con canciones relacionadas con ellas (projectId, SongID, nombre del proyecto, etc.)

  • $ = sTable3 una tabla de calificaciones de canciones (SongID, ID de usuario, valoraciones)

Todos estos datos se envían a una matriz JSON y se muestran en una tabla en mi aplicación para proporcionar una lista de canciones, combinadas con los proyectos y los datos de clasificación.

la propia consulta realiza lo siguiente en este orden: -

  1. Recoge todas las filas de $ estable
  2. se une a $ sTable2 en songsID y cuenta el número de filas (proyectos) en esta tabla que tienen las mismas canciones ID
  3. Se une a $ stable3 en songsID y calcula un promedio de la columna 'clasificación' en esta tabla que tienen las mismas canciones ID
  4. En este punto, también cuenta el número total de filas en $ sTable3 que tienen el mismo ID de canción para proporcionar un número total de votos .
  5. Finalmente realiza una comprobación en todas estas filas para ver si el $ userid (que es una variable que contiene el ID del usuario conectado) coincide con las tiendas 'userid' en $ sTable3 para cada fila para verificar si un el usuario ya ha votado un ID de canción determinado o no. Si coincide, devuelve "Usuario votado" si no devuelve "No votado". Lo muestra como una columna separada en mi matriz JSON, que compruebo contra el lado del cliente en mi aplicación y agrego una clase a.

Si hay más detalles que alguien necesita, por favor házmelo saber. Gracias a todos.

EDIT:

Gracias a la excelente primer intento Aurimis' estoy acercando a una solución mucho más sencilla.

Este es el código que he intentado basándome en esa sugerencia.

SELECT SQL_CALC_FOUND_ROWS ".str_replace(" , ", " ", implode(", ", $aColumns))." 

    FROM 
     (SELECT 
     $sTable.songsID, COUNT(rating) AS ratings_count, 
     AVG(rating) AS ratings 
     FROM $sTable 
     LEFT JOIN $sTable2 ON $sTable.songsID = $sTable2.songs_id 
     LEFT JOIN $sTable3 ON $sTable.songsID = $sTable3.songid 
     GROUP BY $sTable.songsID) AS A 
    LEFT JOIN $sTable3 AS B ON A.songsID = B.songid AND B.userid = $userid 

Sin embargo, hay varios problemas. Tuve que quitar la primera línea de su respuesta ya que causó un error 500 de servidor interno:

IF(B.userid = NULL, "Not voted", "User Voted") AS voted 

Obviamente ahora el 'cheque votaron' funcionalidad se pierde.

Además, y lo que es más importante, no devuelve todas las columnas definidas en mi matriz, solo la ID de canción. My JSON devuelve la columna Desconocida 'song_name' en 'field list' - Si la elimino de mi matriz $ aColumns, pasará a la siguiente.

Estoy definiendo mis columnas al comienzo de mi secuencia de comandos, ya que esta matriz se usa para filtrar y armar la salida para la codificación JSON. Esta es la definición de $ aColumns: -.

$aColumns = array('songsID', 'song_name', 'artist_band_name', 'author', 'song_artwork', 'song_file', 'genre', 'song_description', 'uploaded_time', 'emotion', 'tempo', 'user', 'happiness', 'instruments', 'similar_artists', 'play_count', 'projects_count', 'rating', 'ratings_count', 'voted'); 

Con el fin de probar rápidamente el resto de la consulta modifiqué la primera línea dentro de la sub consulta para seleccionar $ Estable * en lugar de $ sTable.songsID (recordemos $ estables es la tabla de canciones)

Entonces ... La consulta obviamente funcionó, pero con un rendimiento terrible, por supuesto. Pero solo devolvió 24 canciones del conjunto de datos de prueba de 5000 canciones. Por lo tanto, cambié tu primer 'JOIN' a 'LEFT JOIN' para que se devolvieran las 5000 canciones. Para aclarar la consulta, es necesario devolver TODAS las filas en la tabla de canciones, pero con varios bits adicionales de datos de los proyectos y tablas de calificaciones para cada canción.

Así que ... Estamos llegando y estoy seguro de que este es un enfoque mucho mejor, solo necesita algunas modificaciones. Gracias por tu ayuda hasta ahora Aurimis.

+0

parece ser que la persona que escribió la consulta no era nuevo para MySQL – newtover

+0

lo escribí yo mismo por 'googlear', varias preguntas aquí, y leyendo mucho. Sin embargo, en esencia, he unido varias piezas en una consulta de "trabajo" para lograr lo que quiero. Aunque entiendo qué hace cada parte de la consulta y por qué funciona, no tengo la experiencia para saber si estoy haciendo esto de manera eficiente o no. Mi instinto de mi experiencia con otros idiomas me dice que no lo soy. Por lo tanto, agradecería el consejo de los desarrolladores de MySQL más experimentados. :) – gordyr

+0

¿Puedes describir cuál debería ser el resultado esperado? – Peter

Respuesta

3
SELECT SQL_CALC_FOUND_ROWS 
    songsID, song_name, artist_band_name, author, song_artwork, song_file, 
    genre, song_description, uploaded_time, emotion, tempo, 
    `user`, happiness, instruments, similar_artists, play_count, 
    projects_count, 
    rating, ratings_count, 
    IF(user_ratings_count, 'User Voted', 'Not Voted') as voted 
FROM (
    SELECT 
     sp.songsID, projects_count, 
     AVG(rating) as rating, 
     COUNT(rating) AS ratings_count, 
     COUNT(IF(userid=$userid, 1, NULL)) as user_ratings_count 
    FROM (
     SELECT songsID, COUNT(*) as projects_count 
     FROM $sTable s 
     LEFT JOIN $sTable2 p ON s.songsID = p.songs_id 
     GROUP BY songsID) as sp 
    LEFT JOIN $sTable3 r ON sp.songsID = r.songid 
    GROUP BY sp.songsID) as spr 
JOIN $sTable s USING (songsID); 

Necesitará los siguientes índices:

  • (songs_id) en $ sTable2
  • el compuesto (SongID, clasificación, identificación de usuario) en $ sTable3

las ideas detrás de la consulta:

  • subconsultas operan con intercepciones de modo que el resultado de la subconsulta cabría fácilmente en la memoria
  • izquierda se une se agrupan por separado para reducir el producto cartesiano
  • usuario votos se cuentan en la misma subconsulta como otras clasificaciones para evitar caro correlacionados subconsulta
  • toda la información othe se recupera ib la final unirse a
+0

Gracias por la sugerencia de nueva. No estaré en posición de verificar tu solución hasta después del fin de semana. Desde echar un vistazo rápido todo me parece bien, te lo haré saber el lunes. :-) – gordyr

+0

Fantástico ... Funciona a la perfección. Es mucho más simple de entender e infinitamente más elegante en términos de su ejecución. Aún no he realizado ninguna prueba de rendimiento, pero desde la impresión inicial no es peor y usar su consulta hará que cualquier ajuste futuro sea mucho más fácil de administrar, ya que también es estructuralmente superior. Enormes gracias newtover, ¡una respuesta realmente genial! – gordyr

+0

@gordyr, eres bienvenido =) – newtover

1

Déjame intentarlo en función de tu descripción, no de la consulta. Solo utilizaré Songs para indicar Table1, Projects para indicar Table2 y Ratings para indicar Table3 - para mayor claridad.

SELECT 
    /* [column list again] */, 
    IF(B.userid = NULL, "Not voted", "Voted") as voted 
FROM 
    (SELECT 
    Songs.SongID, count(rating) as total_votes, 
    avg(rating) as average_rating /*[,.. other columns as you need them] */ 
    FROM Songs 
    JOIN Projects ON Songs.SongID = Projects.SongID 
    LEFT JOIN Ratings ON Songs.SongID = Ratings.SongID 
    GROUP BY Songs.SongID) as A 
LEFT JOIN Ratings as B ON A.SongID = B.SongID AND B.userid = ? /* your user id */ 

Como se puede ver, se puede obtener toda la información de las canciones en una, relativamente simple consulta (sólo usar GROUP BY y contar) funciones (/ avg()). Para obtener la información de si una canción fue calificada por un usuario en particular requiere una subconsulta, donde puede hacer una unión IZQUIERDA, y si el ID de usuario está vacío, usted sabe que no ha votado.

Ahora, no realicé su consulta en profundidad, ya que realmente parece complicado.Podría ser que me haya perdido algo - si ese es el caso, por favor, actualice la descripción y puedo intentarlo de nuevo :)

+0

Esto se ve muy bien Aurimas, gracias por la sugerencia. No estaré en posición de probar esto por una hora más o menos. Te dejaré saber cómo va. – gordyr

+0

Gracias Aurimas ... He actualizado mi pregunta con mis conclusiones desde que intenté su sugerencia. aclamaciones. :) – gordyr

Cuestiones relacionadas