Como optimizar WP_Query con fields=ids y no_found_rows en WordPress

Contents

Introducción

WP_Query es la clase central de WordPress para recuperar posts. En sitios con muchos contenidos o consultas complejas, una mala configuración puede provocar consultas SQL costosas (JOINs innecesarios, SQL_CALC_FOUND_ROWS, consultas que devuelven muchas filas). Dos parámetros sencillos —fields=ids y no_found_rows— permiten reducir drásticamente el coste de las consultas en muchos casos. Este artículo explica en detalle cómo y cuándo usarlos, riesgos, ejemplos prácticos y patrones avanzados para mantener un sitio WordPress ágil.

Qué hacen fields=ids y no_found_rows

  • fields=ids: modifica la cláusula SELECT para devolver solo los IDs de los posts (SELECT ID FROM …). Menos datos transferidos y menos memoria usada por PHP al construir objetos completos.
  • no_found_rows=true: evita que WP_Query calcule la cantidad total de resultados para paginación. Por defecto WordPress usa SQL_CALC_FOUND_ROWS o una consulta COUNT() adicional para saber cuántos posts existen en total si no necesitas ese número (por ejemplo, en consultas para listados internos o procesamiento por lotes) puedes omitirlo.

Beneficios principales

  • Menor tiempo de ejecución de la consulta.
  • Menos uso de memoria y CPU en PHP porque no se crean objetos WP_Post completos.
  • Menos I/O en la base de datos al transferir menos columnas.
  • Si combinamos con update_post_meta_cache y update_post_term_cache desactivados, evitamos cargas adicionales en caché.

Limitaciones y cuándo no usarlos

  • Paginación visible al usuario: si necesitas mostrar páginas con número total de resultados o enlaces de paginación que muestren el número de páginas, no_found_rows=true romperá esa funcionalidad a menos que calcules el total por separado.
  • Si necesitas campos del post (title, content, meta) inmediatamente, fields=ids obliga a ejecutar consultas adicionales para obtener esos datos.
  • Consultas que dependen del orden por campos que no sean ID pueden requerir cuidado: al solicitar sólo IDs, conviene asegurar que la cláusula ORDER BY devuelva el orden esperado.

Patrones y buenas prácticas

  1. Si solo necesitas IDs para procesar en background (reindexar, enviar emails, regenerar thumbnails), usa fields=ids y no_found_rows=true.
  2. Para listados públicos paginados, evita no_found_rows=true o calcula el total con una consulta COUNT() separada.
  3. Desactiva caches automáticos si no los necesitas: update_post_meta_cache y update_post_term_cache cuando solo trabajas con IDs.
  4. Usa transients o un object cache para almacenar resultados de consultas pesadas (IDs) si se repiten con frecuencia.
  5. Evita meta_query pesadas con comparaciones LIKE sobre campos no indexados si es posible, crea índices o normaliza datos a taxonomías.

Ejemplos prácticos

1) Consulta básica y eficiente para procesamiento en background

Recuperar IDs y procesarlos en lotes sin cargar meta/terms automáticamente:

args = array(
  post_type              => post,
  posts_per_page         => 100,
  fields                 => ids,          // solo IDs
  no_found_rows          => true,           // no calcula total
  update_post_meta_cache => false,          // no cargar meta innecesario
  update_post_term_cache => false,          // no cargar taxonomías
  meta_query             => array(
    array(
      key   => _mi_meta,
      value => valor,
    ),
  ),
)

query = new WP_Query(args)
foreach (query->posts as post_id) {
    // procesar por ID: wp_update_post, wp_delete_post, etc.
}

2) Preservar orden cuando necesitas luego los objetos completos

Patrón: pedir IDs ordenados, después recuperar objetos en el mismo orden con post__in y orden por el orden de post__in.

// 1) Obtener IDs ordenados eficientemente
id_args = array(
  post_type      => product,
  posts_per_page => 50,
  fields         => ids,
  orderby        => date,
  order          => DESC,
  no_found_rows  => true,
)
id_query = new WP_Query(id_args)
ids = id_query->posts

if (!empty(ids)) {
    // 2) Recuperar objetos completos en el mismo orden
    posts = get_posts(array(
      post__in               => ids,
      posts_per_page         => count(ids),
      orderby                => post__in,      // respeta el orden de ids
      update_post_meta_cache => true,            // si necesitas meta
      update_post_term_cache => true,            // si necesitas taxonomías
    ))
    // posts ahora en el mismo orden que ids
}

3) Implementar paginación con conteo por separado

Si necesitas paginación y quieres aun así reducir tamaño de SELECT en la consulta principal, puedes obtener los IDs para la página y calcular el total con una consulta COUNT separada (óptimo cuando la COUNT es mucho más barata que SQL_CALC_FOUND_ROWS en la misma consulta).

global wpdb
paged = max(1, get_query_var(paged))
per_page = 20
offset = (paged - 1)  per_page

// 1) Consulta principal: traer solo IDs de la página
page_ids = wpdb->get_col(wpdb->prepare(
  SELECT ID
  FROM {wpdb->posts}
  WHERE post_type = %s
    AND post_status = publish
  ORDER BY post_date DESC
  LIMIT %d, %d
, post, offset, per_page))

// 2) Conteo total separado (más rápido en algunas configuraciones)
total = (int) wpdb->get_var(wpdb->prepare(
  SELECT COUNT(ID)
  FROM {wpdb->posts}
  WHERE post_type = %s
    AND post_status = publish
, post))

total_pages = ceil(total / per_page)

// 3) Recuperar objetos si hace falta
if (!empty(page_ids)) {
  posts = get_posts(array(
    post__in       => page_ids,
    orderby        => post__in,
    posts_per_page => count(page_ids),
  ))
}

4) Uso en trabajos CRON o batch con límites grandes

Cuando iteras sobre muchos posts, evita cargar todos a la vez usa fields=ids y procesado por lotes para mantener la memoria baja.

per_batch = 500
offset = 0

while (true) {
    q = new WP_Query(array(
      post_type              => post,
      posts_per_page         => per_batch,
      fields                 => ids,
      no_found_rows          => true,
      offset                 => offset,
      update_post_meta_cache => false,
      update_post_term_cache => false,
    ))

    if (empty(q->posts)) {
        break
    }

    foreach (q->posts as post_id) {
        // procesar
    }

    wp_cache_flush() // opcional: liberar memoria del object cache si es necesario
    offset  = per_batch
}

Consideraciones sobre performance SQL

  • SQL_CALC_FOUND_ROWS puede ser costoso en tablas grandes. En muchas configuraciones es más eficiente ejecutar una consulta COUNT separada con índices adecuados.
  • fields=ids reduce ancho de banda y CPU al evitar selección de columnas pesadas (post_content, post_title, post_excerpt) y reduce tiempo de serialización/deserialización en PHP.
  • Desactivar caches automáticos evita llamadas adicionales a WPs cache warming. Reactiva solo si realmente necesitas meta/terms en la fase inmediata.

Errores comunes

  • Usar no_found_rows=true y luego confiar en wp_query->max_num_pages o wp_query->found_posts: esos valores no reflejarán el total correcto.
  • Pensar que fields=ids evita JOINs generados por meta_query o tax_query: los JOINs todavía pueden ocurrir fields=ids solo cambia SELECT, no la estructura de la cláusula FROM/JOIN/WHERE.
  • Solicitar posts_per_page=-1 con fields=ids en tablas gigantes sin paginar: puedes obtener decenas de miles de IDs y aún así saturar memoria o tiempo de ejecución. Mejor procesar por lotes.

Resumen de configuración recomendada

Situación Parámetros recomendados
Proceso en background / reindexar fields=ids, no_found_rows=true, update_post_meta_cache=false, update_post_term_cache=false
Listado público paginado no_found_rows=false (por defecto) — o calcular COUNT por separado si quieres optimizar
Recuperar objetos tras filtrar IDs Primero fields=ids (con orden deseado), luego get_posts con post__in y orderby=post__in

Conclusión

fields=ids y no_found_rows son herramientas potentes para optimizar consultas WP_Query cuando necesitamos eficiencia y no todos los datos del post ni el total de resultados. Usados correctamente (por ejemplo en trabajos en background, APIs internas y procesado por lotes) reducen considerablemente el tiempo de consulta y el consumo de recursos. Sin embargo, hay que ser cuidadoso con la paginación visible y con consultas que requieren meta o taxonomías: en esos casos conviene combinar estrategias (consulta de IDs recuperación ordenada de objetos, o conteo separado con COUNT) para obtener un equilibrio entre rendimiento y funcionalidad.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *