Tip: Optimización de consultas SQL en plugins de WordPress
Por: alex | 10 Diciembre 2007 | Ver comentarios |
Durante el tiempo que no hubo actividad alguna en este blog y por algunos trabajos que me encomendaron relacionados al desarrollo de plugins y modificación del código de WordPress, he visto algo que se repite casi en todos los plugins que he visto hasta el momento: usan varias consultas pequeñas cuando sólo una puede hacer el trabajo (no tengo idea de porqué hacen las cosas de ese modo).
$a = query("select ID from tabla1 ...");
$b = array();
foreach($a as $v) {
$b[] = query("select ... from tabla2 where ID = $v");
}El problema del código mostrado es que se hacen consultas innecesarias a la base de datos cuando en este caso un simple JOIN serviría para el mismo propósito.
Como ejemplo voy a poner dos plugins que acompañaron a este blog desde hace más de un año y por los que el número de consultas SQL aunmentaba en 55 para generar la página principa. Éstos son:
-
Real Fast Latest Comments: En este caso el plugin realiza una consulta para obtener las entradas que recibieron comentarios recientemente y luego itera para mostrar los datos devueltos:
function rflc_show_comments($comment_limit = 5, $show_trackbacks = false) { global $wpdb; if(!$show_trackbacks) { $activity = $wpdb->get_results("SELECT $wpdb->comments.comment_date, $wpdb->comments.comment_author, $wpdb->comments.comment_ID, $wpdb->posts.post_title, $wpdb->posts.ID FROM $wpdb->comments INNER JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID WHERE $wpdb->comments.comment_approved = '1' AND $wpdb->comments.comment_type NOT LIKE '%back%' ORDER BY $wpdb->comments.comment_date DESC LIMIT $comment_limit"); } else { $activity = $wpdb->get_results("SELECT $wpdb->comments.comment_date, $wpdb->comments.comment_author, $wpdb->comments.comment_ID, $wpdb->posts.post_title, $wpdb->posts.ID FROM $wpdb->comments INNER JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID WHERE $wpdb->comments.comment_approved = '1' ORDER BY $wpdb->comments.comment_date DESC LIMIT $comment_limit"); } if($activity) { echo '<ul>'; foreach($activity as $comment) { echo '<li>'.wp_specialchars($comment->comment_author).' en: <a href="'. get_permalink($comment->ID) .'#comment-'. $comment->comment_ID .'">'. wp_specialchars($comment->post_title) .'</a></li>' . "\n"; } echo '</ul>'; } }El problema en este caso es que para mostrar el enlace (permalink) de una entrada, el plugin invoca en cada iteración a la función
get_permalink, la misma que hace una nueva consulta (o varias dependiendo de la estructura de permalinks) para recuperar la entrada y formatear el enlace, esto pasa siempre y cuando el argumento pasado no sea un objeto o la entrada ya se encuentre en la caché de objetos. -
Related Posts: El problema es el mismo que se comenta en el anterior, a continuación muestro la parte relevante:
$sql = "SELECT ID, post_title, post_content," . "MATCH (post_name, post_content) " . "AGAINST ('$terms') AS score " . "FROM $wpdb->posts WHERE " . "MATCH (post_name, post_content) " . "AGAINST ('$terms') " . "AND post_date <= '$now' " . "AND (post_status IN ( 'publish', 'static' ) && ID != '$post->ID') "; if ($show_pass_post=='false') { $sql .= "AND post_password ='' "; } $sql .= "ORDER BY score DESC LIMIT $limit"; $results = $wpdb->get_results($sql); $output = ''; if ($results) { foreach ($results as $result) { $title = stripslashes(apply_filters('the_title', $result->post_title)); $permalink = get_permalink($result->ID); $post_content = strip_tags($result->post_content); $post_content = stripslashes($post_content); $output .= $before_title .'<a href="'. $permalink .'" rel="bookmark" title="Permanent Link: ' . $title . '">' . $title . '</a>' . $after_title; if ($show_excerpt=='true') { $words=split(" ",$post_content); $post_strip = join(" ", array_slice($words,0,$len)); $output .= $before_post . $post_strip . $after_post; } } echo $output; }
Para evitar este comportamiento pero con algunas posibles consecuencias no deseadas**, se debe recuperar también el campo post_name en la primera consulta de ambos casos y a continuación invocar a la función get_permalink con un objeto como parámetro — get_permalink($comment); y get_permalink($result); respectivamente.
**: Al usar la función get_permalink con un objeto como parámetro, éste se almacena en el caché de objetos y cualquier función que haga uso de get_post puede mostrar entradas “incompletas”. Una forma de evitar esto es recuperar todos los campos de la tabla posts o quitar la entrada almacenada en cache luego de invocar a la función get_permalink.
Tags: get_permalink, plugin, sql, Wordpress


maty
10 de Diciembre de 2007, 06:00:23 am
Precisamente es algo que quería retocar en UTI, al ser la base de datos grande y compleja, lo que ralentiza la generación de la página con la búsqueda avanzada (si no está en la caché).
http://unatemporadaenelinfierno.net/advanced-search/
En cambio, con el plugin ELA, es más rápido:
http://unatemporadaenelinfierno.net/archives/
Llevo mucho tiempo quejándome de que las búsquedas han de mejorarse mucho en todos los CMS bitacoriles, empezando por WP (en ello están para la 2.4 leí).
Pasan los años, y las bitácoras que perduran comienzan a tener unas bases de datos de entidad, que no son suficientemente bien gestionadas, de ahí que para montar un digital “casero” se recurra a otras opciones.