{"id":167176,"date":"2025-07-28T09:00:00","date_gmt":"2025-07-28T09:00:00","guid":{"rendered":"https:\/\/gtechgroup.it\/blog\/acf-query-avanzate-wp-query-meta-query\/"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T22:00:00","slug":"acf-query-avanzate-wp-query-meta-query","status":"publish","type":"post","link":"https:\/\/gtechgroup.it\/blog\/acf-query-avanzate-wp-query-meta-query\/","title":{"rendered":"Query Avanzate con ACF: WP_Query, Meta Query e Performance"},"content":{"rendered":"<h2>Query sui Campi ACF: Fondamenti e Strategie<\/h2>\n<p>Una delle esigenze pi&#249; comuni quando si lavora con Advanced Custom Fields &#232; la necessit&#224; di <strong>interrogare il database<\/strong> per recuperare post in base ai valori dei campi personalizzati. Vuoi mostrare tutti gli eventi futuri ordinati per data? Tutti i prodotti con un prezzo inferiore a 100 euro? Tutti gli immobili in una determinata citt&#224; con almeno 3 camere? Tutte queste operazioni richiedono l&#8217;uso di <strong>WP_Query<\/strong> con <strong>meta_query<\/strong>, lo strumento di WordPress per filtrare i contenuti in base ai metadati.<\/p>\n<p>Tuttavia, le query sui metadati presentano sfide significative in termini di <strong>performance<\/strong>, soprattutto quando il database cresce e le condizioni di filtro si moltiplicano. La tabella <code>wp_postmeta<\/code>, dove ACF salva tutti i suoi dati, utilizza il modello EAV (Entity-Attribute-Value) che, pur essendo flessibile, non &#232; ottimizzato per query complesse con molti filtri. In questo articolo analizzeremo come costruire query efficaci, come ottimizzarle per le performance e quali strategie adottare quando le meta query non bastano.<\/p>\n<h2>WP_Query e Meta Query: La Sintassi Base<\/h2>\n<p>WordPress permette di filtrare i risultati di <code>WP_Query<\/code> in base ai metadati attraverso il parametro <code>meta_query<\/code>. Ecco un esempio di query che recupera tutti i post con un campo ACF &#8220;prezzo&#8221; inferiore a 100:<\/p>\n<pre><code>&lt;?php\n$args = array(\n    post_type      =&gt; prodotto,\n    posts_per_page =&gt; 20,\n    meta_query     =&gt; array(\n        array(\n            key     =&gt; prezzo,\n            value   =&gt; 100,\n            compare =&gt; &lt;,\n            type    =&gt; NUMERIC,\n        ),\n    ),\n);\n$query = new WP_Query($args);\n\nif($query-&gt;have_posts()):\n    while($query-&gt;have_posts()): $query-&gt;the_post();\n        echo &lt;h2&gt; . get_the_title() . &lt;\/h2&gt;;\n        echo &lt;p&gt;Prezzo: &amp;euro; . get_field(prezzo) . &lt;\/p&gt;;\n    endwhile;\n    wp_reset_postdata();\nendif;\n?&gt;<\/code><\/pre>\n<p>I parametri principali di ogni condizione meta_query sono: <strong>key<\/strong> (il nome del campo ACF, corrispondente al meta_key nel database), <strong>value<\/strong> (il valore da confrontare), <strong>compare<\/strong> (l&#8217;operatore di confronto) e <strong>type<\/strong> (il tipo di dato per il confronto). Gli operatori di confronto disponibili sono: <code>=<\/code>, <code>!=<\/code>, <code>&gt;<\/code>, <code>&gt;=<\/code>, <code>&lt;<\/code>, <code>&lt;=<\/code>, <code>LIKE<\/code>, <code>NOT LIKE<\/code>, <code>IN<\/code>, <code>NOT IN<\/code>, <code>BETWEEN<\/code>, <code>NOT BETWEEN<\/code>, <code>EXISTS<\/code> e <code>NOT EXISTS<\/code>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/gtechgroup.it\/blog\/wp-content\/uploads\/2026\/05\/acf-01-groups.png\" alt=\"Gestione avanzata dei campi ACF per le query sul database WordPress\" \/><\/p>\n<p>Il parametro <strong>type<\/strong> &#232; fondamentale e spesso trascurato. I valori nella tabella <code>wp_postmeta<\/code> sono sempre salvati come stringhe, indipendentemente dal tipo di campo ACF. Senza specificare il tipo, il confronto avviene come stringa, il che pu&#242; produrre risultati errati per i numeri: ad esempio, &#8220;9&#8221; risulterebbe maggiore di &#8220;100&#8221; in un confronto stringa. Specificando <code>type =&gt; NUMERIC<\/code> (o <code>DECIMAL<\/code>, <code>SIGNED<\/code>, <code>UNSIGNED<\/code>), WordPress esegue un CAST nel database per trattare i valori come numeri.<\/p>\n<h2>Meta Query Complesse: AND e OR<\/h2>\n<p>Per filtrare in base a pi&#249; campi contemporaneamente, puoi combinare condizioni con la logica <strong>AND<\/strong> (tutte le condizioni devono essere vere) oppure <strong>OR<\/strong> (almeno una condizione deve essere vera). Il parametro <strong>relation<\/strong> all&#8217;interno di <code>meta_query<\/code> controlla la logica:<\/p>\n<pre><code>&lt;?php\n\/\/ Prodotti con prezzo &lt; 100 E in stock\n$args = array(\n    post_type  =&gt; prodotto,\n    meta_query =&gt; array(\n        relation =&gt; AND,\n        array(\n            key     =&gt; prezzo,\n            value   =&gt; 100,\n            compare =&gt; &lt;,\n            type    =&gt; NUMERIC,\n        ),\n        array(\n            key     =&gt; in_stock,\n            value   =&gt; 1,\n            compare =&gt; =,\n        ),\n    ),\n);\n?&gt;<\/code><\/pre>\n<p>Puoi anche annidare gruppi di condizioni per logiche pi&#249; complesse. Ad esempio: &#8220;prodotti con prezzo tra 50 e 200 E (colore rosso OPPURE colore blu)&#8221;. Questo si traduce in un meta_query con relation AND al primo livello e un sotto-gruppo con relation OR. Ogni livello di annidamento aggiunge un JOIN sulla tabella postmeta nella query SQL risultante, il che ha implicazioni significative sulle performance che analizzeremo pi&#249; avanti.<\/p>\n<h2>Ordinare per Valore del Campo ACF<\/h2>\n<p>Per ordinare i risultati in base al valore di un campo ACF, usa i parametri <code>meta_key<\/code>, <code>orderby<\/code> e <code>order<\/code> di WP_Query:<\/p>\n<pre><code>&lt;?php\n\/\/ Eventi ordinati per data (dal pi&#249; prossimo)\n$args = array(\n    post_type  =&gt; evento,\n    meta_key   =&gt; data_evento,\n    orderby    =&gt; meta_value,\n    order      =&gt; ASC,\n    meta_query =&gt; array(\n        array(\n            key     =&gt; data_evento,\n            value   =&gt; date(Ymd),\n            compare =&gt; &gt;=,\n        ),\n    ),\n);\n?&gt;<\/code><\/pre>\n<p>Per l&#8217;ordinamento numerico, usa <code>orderby =&gt; meta_value_num<\/code> invece di <code>meta_value<\/code>. Questo &#232; essenziale per campi che contengono numeri (prezzi, quantit&#224;, valutazioni) per evitare l&#8217;ordinamento lessicografico. Puoi anche ordinare per pi&#249; campi contemporaneamente usando meta query con nome (named meta queries) e un array nel parametro orderby.<\/p>\n<pre><code>&lt;?php\n$args = array(\n    post_type  =&gt; prodotto,\n    meta_query =&gt; array(\n        prezzo_clause =&gt; array(\n            key  =&gt; prezzo,\n            type =&gt; NUMERIC,\n        ),\n        rating_clause =&gt; array(\n            key  =&gt; valutazione,\n            type =&gt; NUMERIC,\n        ),\n    ),\n    orderby =&gt; array(\n        rating_clause =&gt; DESC,\n        prezzo_clause =&gt; ASC,\n    ),\n);\n?&gt;<\/code><\/pre>\n<h2>Query su Campi Specifici di ACF<\/h2>\n<p>Diversi tipi di campo ACF richiedono attenzioni particolari nelle query. I campi <strong>Date Picker<\/strong> salvano le date nel formato <code>Ymd<\/code> (es. &#8220;20260315&#8221;), che &#232; perfetto per i confronti cronologici con gli operatori <code>&lt;<\/code>, <code>&gt;<\/code>, <code>BETWEEN<\/code>. Per trovare eventi in un range di date specifico:<\/p>\n<pre><code>array(\n    key     =&gt; data_evento,\n    value   =&gt; array(20260301, 20260331),\n    compare =&gt; BETWEEN,\n)<\/code><\/pre>\n<p><img decoding=\"async\" src=\"https:\/\/gtechgroup.it\/blog\/wp-content\/uploads\/2026\/05\/acf-02-newgroup.png\" alt=\"Configurazione dei campi ACF ottimizzati per le query avanzate\" \/><\/p>\n<p>I campi <strong>True\/False<\/strong> salvano <code>1<\/code> (vero) o <code>0<\/code> (falso, o campo non compilato). Per trovare i post con un campo True\/False attivo: <code>key =&gt; in_evidenza, value =&gt; 1, compare =&gt; =<\/code>. Attenzione: un campo True\/False mai salvato non ha una riga in postmeta (a differenza di un campo esplicitamente impostato su false che ha valore &#8220;0&#8221;). Se vuoi trovare i post dove il campo &#232; false O non esiste, usa l&#8217;operatore <code>NOT EXISTS<\/code> o una logica OR combinata.<\/p>\n<p>I campi <strong>Select<\/strong> e <strong>Checkbox<\/strong> salvano i valori selezionati come stringhe. Per i Select a selezione singola, il confronto &#232; diretto: <code>value =&gt; rosso, compare =&gt; =<\/code>. Per i Checkbox (selezione multipla), i valori sono salvati come array serializzato, il che rende le query pi&#249; complesse: devi usare <code>LIKE<\/code> con il valore racchiuso tra virgolette serializzate: <code>value =&gt; \"rosso\", compare =&gt; LIKE<\/code>. Questo &#232; un&#8217;area dove le meta query mostrano i loro limiti: le query LIKE su dati serializzati sono lente e non sfruttano gli indici del database.<\/p>\n<h2>Performance delle Meta Query: Il Problema<\/h2>\n<p>Le meta query sono lo strumento pi&#249; diretto per filtrare i post in base ai campi ACF, ma hanno un <strong>costo di performance significativo<\/strong>. La tabella <code>wp_postmeta<\/code> utilizza il modello EAV dove ogni campo &#232; una riga separata. Per filtrare per un singolo campo, WordPress esegue un JOIN tra <code>wp_posts<\/code> e <code>wp_postmeta<\/code>. Per filtrare per due campi contemporaneamente, servono due JOIN. Per tre campi, tre JOIN. E cos&#236; via.<\/p>\n<p>Ogni JOIN aggiuntivo aumenta esponenzialmente il tempo di esecuzione della query, specialmente quando la tabella postmeta contiene milioni di righe (cosa comune in siti con migliaia di post e decine di campi ACF per post). Una query con 5 condizioni meta_query pu&#242; richiedere secondi interi per essere eseguita su un database di medie dimensioni, rendendo impossibile un&#8217;esperienza utente fluida per pagine con filtri in tempo reale.<\/p>\n<p>Per visualizzare la query SQL generata e capire dove sono i colli di bottiglia, puoi abilitare il debug delle query aggiungendo <code>define(SAVEQUERIES, true)<\/code> al file <code>wp-config.php<\/code> (solo in ambiente di sviluppo!) e poi ispezionare <code>$wpdb-&gt;queries<\/code>. Plugin come Query Monitor ti permettono di vedere le query SQL, il loro tempo di esecuzione e i possibili problemi di performance direttamente nella toolbar di WordPress.<\/p>\n<h2>Strategie di Ottimizzazione<\/h2>\n<p>Esistono diverse strategie per migliorare le performance delle query sui campi ACF. La prima e pi&#249; efficace &#232; <strong>ridurre il numero di condizioni meta_query<\/strong>. Se puoi spostare alcuni filtri a livello di tassonomia (categorie, tag, tassonomie personalizzate), le query saranno molto pi&#249; efficienti perch&#233; le tabelle delle tassonomie sono ottimizzate per il filtraggio con indici appropriati.<\/p>\n<p>La seconda strategia &#232; <strong>aggiungere indici personalizzati<\/strong> alla tabella <code>wp_postmeta<\/code>. L&#8217;indice predefinito copre <code>meta_key<\/code>, ma un indice composto su <code>(meta_key, meta_value)<\/code> pu&#242; migliorare significativamente le performance delle query con confronti di uguaglianza. Attenzione: questa modifica &#232; a livello di database e pu&#242; avere effetti collaterali sulle operazioni di scrittura.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/gtechgroup.it\/blog\/wp-content\/uploads\/2026\/05\/acf-03-tools.png\" alt=\"Strumenti di ottimizzazione per le query avanzate con ACF\" \/><\/p>\n<p>La terza strategia &#232; implementare il <strong>caching dei risultati<\/strong>. Puoi usare la Transients API di WordPress per cachare i risultati delle query pi&#249; costose: <code>set_transient(prodotti_scontati, $risultati, HOUR_IN_SECONDS)<\/code>. Il transient viene invalidato dopo il tempo specificato, e puoi anche invalidarlo manualmente quando i dati cambiano (ad esempio, nell&#8217;hook <code>acf\/save_post<\/code>). Questa strategia &#232; particolarmente efficace per query che vengono eseguite frequentemente ma i cui risultati cambiano raramente.<\/p>\n<h2>Tabelle Personalizzate come Alternativa<\/h2>\n<p>Per siti con esigenze di performance critiche, la soluzione pi&#249; radicale &#232; abbandonare la tabella <code>wp_postmeta<\/code> e utilizzare <strong>tabelle personalizzate<\/strong> nel database. Con questo approccio, crei una tabella dedicata (ad esempio <code>wp_prodotti_meta<\/code>) con una colonna per ogni campo, e salvi\/leggi i dati direttamente da questa tabella. Le query diventano semplici SELECT con WHERE sulle colonne, senza JOIN, con performance ordini di grandezza superiori.<\/p>\n<p>ACF non supporta nativamente le tabelle personalizzate, ma puoi implementare questa soluzione con codice personalizzato utilizzando gli hook <code>acf\/save_post<\/code> (per scrivere nella tabella personalizzata quando un post viene salvato) e <code>acf\/pre_load_value<\/code> (per leggere dalla tabella personalizzata invece che da postmeta). In alternativa, plugin come <strong>Meta Box<\/strong> con l&#8217;addon Custom Table offrono questa funzionalit&#224; out of the box.<\/p>\n<p>La decisione di passare alle tabelle personalizzate deve essere ponderata attentamente. I vantaggi in performance sono significativi, ma gli svantaggi includono: complessit&#224; aggiuntiva nella gestione del database, perdita di compatibilit&#224; con plugin che si aspettano i dati in postmeta, necessit&#224; di una logica di migrazione per i dati esistenti, e maggiore difficolt&#224; nel debugging. Per la maggior parte dei siti, le strategie di ottimizzazione meno invasive (caching, indici, tassonomie) sono sufficienti.<\/p>\n<h2>La Funzione acf_query() e le API Avanzate<\/h2>\n<p>ACF non fornisce una funzione di query dedicata, ma le sue funzioni interne possono essere sfruttate per ottimizzare il recupero dei dati. La funzione <code>get_field()<\/code> internamente utilizza il caching dell&#8217;oggetto per evitare query ripetute: se chiami <code>get_field(prezzo, $post_id)<\/code> pi&#249; volte per lo stesso post, la query al database viene eseguita solo la prima volta.<\/p>\n<p>Per precaricare i metadati di pi&#249; post in una singola query (riducendo il problema N+1), puoi usare la funzione nativa di WordPress <code>update_post_meta_cache($post_ids)<\/code> che carica tutti i metadati degli ID specificati in una singola query. Questo &#232; particolarmente utile nelle pagine archivio dove mostri una lista di post con i loro campi ACF: invece di una query per campo per post, ottieni tutti i dati con una singola query aggiuntiva.<\/p>\n<p>Le query avanzate con ACF richiedono un equilibrio tra funzionalit&#224; e performance. Per i casi semplici, le meta query standard funzionano perfettamente. Per i casi complessi, le strategie di ottimizzazione descritte in questo articolo ti permetteranno di mantenere tempi di risposta accettabili anche con dataset di grandi dimensioni. La chiave &#232; misurare, ottimizzare e misurare di nuovo, utilizzando strumenti come Query Monitor per identificare i colli di bottiglia reali.<\/p>\n<div style=\"background:#f9f9f9;border:1px solid #e0e0e0;padding:20px;margin:30px 0;border-radius:8px;\">\n<h3>Leggi anche gli altri articoli della serie ACF<\/h3>\n<ul>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/installare-configurare-acf-advanced-custom-fields\/\">Come Installare e Configurare Advanced Custom Fields su WordPress<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-creare-primo-gruppo-campi-personalizzati\/\">ACF: Creare il Primo Gruppo di Campi Personalizzati<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-tipi-campo-testo-immagine-relazione\/\">Tutti i Tipi di Campo ACF: Testo, Immagine, Relazione e Altro<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-repeater-field-contenuti-ripetibili\/\">ACF e i Repeater Field: Creare Contenuti Ripetibili e Flessibili<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-options-page-impostazioni-globali-tema\/\">ACF e le Options Page: Impostazioni Globali per il Tema<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-visualizzare-campi-frontend-template-shortcode\/\">Visualizzare i Campi ACF nel Frontend: Template e Shortcode<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-elementor-campi-personalizzati-page-builder\/\">ACF e Elementor: Usare i Campi Personalizzati nel Page Builder<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-woocommerce-campi-personalizzati-prodotti\/\">ACF e WooCommerce: Aggiungere Campi Personalizzati ai Prodotti<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-flexible-content-layout-dinamici\/\">ACF Flexible Content: Layout Dinamici Senza Page Builder<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-gutenberg-blocchi-custom-campi\/\">ACF e Gutenberg: Blocchi Custom con Campi Personalizzati<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-free-vs-pro-confronto-prezzi\/\">ACF Free vs Pro: Confronto Funzionalit&#224; e Prezzi 2026<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-vs-custom-fields-vs-meta-box-vs-pods\/\">ACF vs Custom Fields Nativi vs Meta Box vs Pods: Confronto<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-import-export-sincronizzazione-json\/\">ACF: Import, Export e Sincronizzazione JSON dei Field Group<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/acf-problemi-comuni-troubleshooting\/\">Risolvere i Problemi Comuni di ACF: Troubleshooting<\/a><\/li>\n<\/ul>\n<\/div>\n<p>Per implementare query avanzate e ottimizzare le performance del tuo sito WordPress con ACF, il team di <a href=\"https:\/\/gtechgroup.it\/realizzazione-siti-web\/\">G Tech Group &#232; specializzato nella realizzazione di siti web<\/a> ad alte prestazioni con architetture dati ottimizzate. <a href=\"https:\/\/gtechgroup.it\/contatti\/\">Contattaci<\/a> per una consulenza tecnica e scopri come possiamo rendere il tuo sito veloce e scalabile.<\/p>\n<h3>Migliora il Tuo Sito WordPress<\/h3>\n<p>Scopri le nostre guide complete sugli altri plugin essenziali per WordPress:<\/p>\n<ul>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/come-installare-elementor-wordpress-guida-principianti\/\">Come Installare Elementor su WordPress<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/wp-rocket-installare-configurare-wordpress\/\">Come Installare e Configurare WP Rocket<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/come-installare-configurare-seopress-wordpress-guida\/\">Come Installare e Configurare SEOPress<\/a><\/li>\n<li><a href=\"https:\/\/gtechgroup.it\/blog\/installare-attivare-updraftplus-wordpress\/\">Come Installare e Configurare UpdraftPlus<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Query sui Campi ACF: Fondamenti e Strategie Una delle esigenze pi&#249; comuni quando si lavora con Advanced Custom Fields &#232; la necessit&#224; di interrogare il&hellip;<\/p>\n","protected":false},"author":0,"featured_media":167114,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_seopress_robots_primary_cat":"","_seopress_titles_title":"ACF Query Avanzate: WP_Query e Performance | Guida","_seopress_titles_desc":"Come fare query avanzate con campi ACF. WP_Query, meta_query, performance e ottimizzazione database.","_seopress_robots_index":"","footnotes":""},"categories":[1],"tags":[2602,2891,3563,1775,492,3562],"class_list":["post-167176","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-senza-categoria","tag-acf","tag-database","tag-meta-query","tag-performance","tag-wordpress","tag-wp_query"],"_links":{"self":[{"href":"https:\/\/gtechgroup.it\/blog\/wp-json\/wp\/v2\/posts\/167176","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gtechgroup.it\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gtechgroup.it\/blog\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/gtechgroup.it\/blog\/wp-json\/wp\/v2\/comments?post=167176"}],"version-history":[{"count":0,"href":"https:\/\/gtechgroup.it\/blog\/wp-json\/wp\/v2\/posts\/167176\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/gtechgroup.it\/blog\/wp-json\/wp\/v2\/media\/167114"}],"wp:attachment":[{"href":"https:\/\/gtechgroup.it\/blog\/wp-json\/wp\/v2\/media?parent=167176"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gtechgroup.it\/blog\/wp-json\/wp\/v2\/categories?post=167176"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gtechgroup.it\/blog\/wp-json\/wp\/v2\/tags?post=167176"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}