Como limpar um banco WordPress herdado com segurança: CPTs antigos, ACF, taxonomias, revisões, transients e tabelas
Para deixar sua aplicação rodando com maior eficiência sob o ponto de vista de banco de dados vou explicar o que faço com meus projetos

Índice
- ETAPA 1: Descobrir quais post types ainda existem no banco
- ETAPA 2: Não confunda CPT legado com post types internos
- ETAPA 3: Conferir os posts do CPT suspeito
- ETAPA 4: Descobrir quanto postmeta/ACF esse CPT possui
- ETAPA 5: Descobrir as taxonomias relacionadas ao CPT
- ETAPA 6: Conferir os termos da taxonomia antiga
- ETAPA 7: Por que excluir pelo WordPress e não diretamente por SQL
- ETAPA 8: Registrar temporariamente CPT e taxonomia removidos
- ETAPA 9: Executar a limpeza
- ETAPA 10: Confirmar que o CPT realmente desapareceu
- ETAPA 11: Limpar revisões
- ETAPA 12: Limpeza de resíduos das tabelas
- ETAPA 13: Limpar transients
- ETAPA 14: Conferir novamente os post types
- ETAPA 15: Otimizar as tabelas
- ETAPA 16: Medir o tamanho das tabelas
- Checklist rápido para repetir em outro WordPress
- Estratégia resumida
Quando um site WordPress é reaproveitado de outro projeto, trocar o tema, remover register_post_type(), apagar CPTs do código ou excluir campos do ACF não significa que os dados antigos desapareceram do banco.
É comum o banco continuar carregando:
- posts de CPTs que não existem mais no código;
- milhares de linhas de
wp_postmeta; - valores antigos de ACF;
- relacionamentos de taxonomias;
- termos de taxonomias antigas;
- revisões;
- transients;
- registros órfãos;
- espaço não recuperado nas tabelas.
O processo seguro é dividir a limpeza em quatro fases:
Cadastre-se para receber nossos conteúdos por e-mail
- inventariar o banco com SQL;
- apagar conteúdos legados pela API do WordPress;
- limpar resíduos e caches com SQL;
- otimizar as tabelas.
A regra principal é:
SQL para descobrir e limpar resíduos. WordPress para excluir entidades reais.
Césinha
Isso é importante porque wp_delete_post() remove não apenas o registro do post, mas também comentários, post meta e relações com termos vinculadas àquele post.
ANTES DE TUDO: Faça backup antes de qualquer coisa
Antes de executar qualquer DELETE:
- exporte o banco pelo phpMyAdmin;
- ou gere um backup pela hospedagem;
- ou use WP-CLI/MySQL.
Não pule essa etapa.
As queries abaixo são seguras quando aplicadas corretamente, mas estamos falando de exclusão definitiva.
ETAPA 1: Descobrir quais post types ainda existem no banco
Comece pelo phpMyAdmin.
Execute:
%%PB_PROTECTED_0%%
SELECT
post_type,
COUNT(*) AS total
FROM wp_posts
GROUP BY post_type
ORDER BY post_type;SQLSe seu prefixo for diferente de wp_, ajuste os nomes das tabelas.
O resultado pode mostrar algo assim:
%%PB_PROTECTED_1%%
acf-field
acf-field-group
attachment
cartao_credito
experiencias
nav_menu_item
page
post
revision
videos
wp_global_styles
...SQLO detalhe importante é que essa consulta mostra os registros fisicamente existentes no banco, mesmo que o CPT não esteja mais registrado no PHP.
No nosso caso, apareceu:
%%PB_PROTECTED_2%%
experiencias → 15SQLEmbora o CPT já tivesse sido removido do projeto atual.
ETAPA 2: Não confunda CPT legado com post types internos
Não saia apagando tudo que você não reconhece.
Alguns post_type são internos do WordPress ou de plugins e devem permanecer.
Exemplos:
%%PB_PROTECTED_3%%
post
page
attachment
revision
nav_menu_item
customize_changeset
oembed_cache
acf-field
acf-field-group
acf-ui-options-page
wp_global_styles
wp_navigationSQLAlém deles, preserve naturalmente os CPTs atuais do projeto.
O objetivo é identificar algo claramente herdado, por exemplo:
%%PB_PROTECTED_4%%
experiencias
promocoes
lazer
hotel
destinosSQLquando esses tipos não existem mais no site atual.
ETAPA 3: Conferir os posts do CPT suspeito
Antes de excluir qualquer CPT, veja os conteúdos.
Exemplo:
%%PB_PROTECTED_5%%
SELECT
ID,
post_type,
post_status,
post_title,
post_date,
post_parent
FROM wp_posts
WHERE post_type = 'experiencias'
ORDER BY ID;SQLIsso permite reconhecer visualmente se são realmente conteúdos do projeto antigo.
Você também pode consultar vários CPTs:
%%PB_PROTECTED_6%%
SELECT
ID,
post_type,
post_status,
post_title,
post_date
FROM wp_posts
WHERE post_type IN (
'experiencias',
'promocoes',
'lazer'
)
ORDER BY post_type, ID;SQLSó avance depois de confirmar que esses conteúdos podem desaparecer.
ETAPA 4: Descobrir quanto postmeta/ACF esse CPT possui
Os valores ACF normalmente ficam em wp_postmeta.
Para saber quanto aquele CPT carrega:
%%PB_PROTECTED_7%%
SELECT
COUNT(*) AS total_metadados
FROM wp_postmeta pm
INNER JOIN wp_posts p
ON p.ID = pm.post_id
WHERE p.post_type = 'experiencias';SQLPara enxergar as chaves:
%%PB_PROTECTED_8%%
SELECT
pm.meta_key,
COUNT(*) AS total
FROM wp_postmeta pm
INNER JOIN wp_posts p
ON p.ID = pm.post_id
WHERE p.post_type = 'experiencias'
GROUP BY pm.meta_key
ORDER BY total DESC, pm.meta_key;SQLEm ACF é comum encontrar pares como:
%%PB_PROTECTED_9%%
imagem_experiencia
_imagem_experiencia
titulo_experiencia
_titulo_experienciaSQLO campo sem _ contém o valor.
O campo começando com _ normalmente contém a referência:
%%PB_PROTECTED_10%%
field_64abc123...SQLEsses metas não precisam ser apagados manualmente quando você remove corretamente o post com WordPress.
ETAPA 5: Descobrir as taxonomias relacionadas ao CPT
Antes de apagar o CPT, descubra quais taxonomias ele utiliza:
%%PB_PROTECTED_11%%
SELECT
tt.taxonomy,
COUNT(DISTINCT tr.object_id) AS posts_relacionados,
COUNT(*) AS relacionamentos
FROM wp_term_relationships tr
INNER JOIN wp_posts p
ON p.ID = tr.object_id
INNER JOIN wp_term_taxonomy tt
ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE p.post_type = 'experiencias'
GROUP BY tt.taxonomy
ORDER BY tt.taxonomy;SQLNo nosso caso encontramos:
%%PB_PROTECTED_12%%
tipo_experiencia
15 posts relacionados
20 relacionamentosSQLIsso indicava uma taxonomia claramente ligada ao CPT legado.
ETAPA 6: Conferir os termos da taxonomia antiga
Antes de removê-la:
%%PB_PROTECTED_13%%
SELECT
t.term_id,
t.name,
t.slug,
tt.taxonomy,
tt.count
FROM wp_terms t
INNER JOIN wp_term_taxonomy tt
ON tt.term_id = t.term_id
WHERE tt.taxonomy = 'tipo_experiencia'
ORDER BY t.name;SQLAssim você consegue confirmar os termos que serão removidos.
ETAPA 7: Por que excluir pelo WordPress e não diretamente por SQL
Não recomendo fazer apenas:
%%PB_PROTECTED_14%%
DELETE FROM wp_posts
WHERE post_type = 'experiencias';SQLPorque um post possui dependências em várias tabelas.
A melhor opção é:
%%PB_PROTECTED_15%%
wp_delete_post($post_id, true);SQLCom true, a exclusão é definitiva.
O próprio WordPress remove também os dados associados, incluindo:
- comentários;
- post meta;
- valores ACF;
- referências ACF;
- relações com termos.
Isso é comportamento documentado do wp_delete_post().
ETAPA 8: Registrar temporariamente CPT e taxonomia removidos
Há um detalhe importante.
Se você já removeu:
%%PB_PROTECTED_16%%
register_post_type('experiencias', ...);SQLe:
%%PB_PROTECTED_17%%
register_taxonomy('tipo_experiencia', ...);SQLdo projeto atual, o WordPress pode não reconhecer mais essas estruturas.
Por isso, registre-as temporariamente durante a limpeza.
Coloque temporariamente no final do functions.php:
%%PB_PROTECTED_18%%
<?php
if (!defined('ABSPATH')) {
exit;
}
/**
* ======================================================
* LIMPEZA TEMPORÁRIA
*
* CPT:
* experiencias
*
* Taxonomia:
* tipo_experiencia
*
* Remover este código após executar.
* ======================================================
*/
/* ======================================================
* REGISTRO TEMPORÁRIO
* ====================================================== */
add_action('init', function () {
if (!post_type_exists('experiencias')) {
register_post_type('experiencias', [
'public' => false,
'show_ui' => false,
]);
}
if (!taxonomy_exists('tipo_experiencia')) {
register_taxonomy(
'tipo_experiencia',
['experiencias'],
[
'public' => false,
'show_ui' => false,
]
);
}
}, 1);
/* ======================================================
* EXECUTA A LIMPEZA
* ====================================================== */
add_action('admin_init', function () {
if (!current_user_can('manage_options')) {
return;
}
if (!isset($_GET['cmv_limpar_experiencias'])) {
return;
}
/* ======================================================
* REMOVE POSTS
* ====================================================== */
$posts_experiencias = get_posts([
'post_type' => 'experiencias',
'post_status' => 'any',
'posts_per_page' => -1,
'fields' => 'ids',
'orderby' => 'ID',
'order' => 'ASC',
]);
$posts_excluidos = 0;
foreach ($posts_experiencias as $post_id) {
$resultado = wp_delete_post($post_id, true);
if ($resultado) {
$posts_excluidos++;
}
}
/* ======================================================
* REMOVE TERMOS
* ====================================================== */
$termos_excluidos = 0;
$termos = get_terms([
'taxonomy' => 'tipo_experiencia',
'hide_empty' => false,
'fields' => 'ids',
]);
if (!is_wp_error($termos)) {
foreach ($termos as $term_id) {
$resultado = wp_delete_term(
$term_id,
'tipo_experiencia'
);
if ($resultado && !is_wp_error($resultado)) {
$termos_excluidos++;
}
}
}
/* ======================================================
* RESULTADO
* ====================================================== */
wp_die(
'<h1>Limpeza concluída</h1>' .
'<p><strong>Posts removidos:</strong> ' .
esc_html($posts_excluidos) .
'</p>' .
'<p><strong>Termos removidos:</strong> ' .
esc_html($termos_excluidos) .
'</p>' .
'<p><strong>Remova agora este código do functions.php.</strong></p>'
);
});PHPA remoção dos termos com wp_delete_term() também elimina os metadados associados ao termo.
ETAPA 9: Executar a limpeza
Depois de colocar o código, acesse como administrador:
%%PB_PROTECTED_19%%
https://seusite.com.br/wp-admin/?cmv_limpar_experiencias=1SQLNo nosso caso o resultado foi:
%%PB_PROTECTED_20%%
Posts "experiencias" removidos: 15
Termos "tipo_experiencia" removidos: 5SQLTerminou?
Remova imediatamente esse código do functions.php.
Não deixe uma rotina de exclusão administrativa disponível no projeto.
ETAPA 10: Confirmar que o CPT realmente desapareceu
Volte ao SQL:
%%PB_PROTECTED_21%%
SELECT COUNT(*) AS experiencias_restantes
FROM wp_posts
WHERE post_type = 'experiencias';SQLEsperado:
%%PB_PROTECTED_22%%
0SQLE:
%%PB_PROTECTED_23%%
SELECT COUNT(*) AS termos_restantes
FROM wp_term_taxonomy
WHERE taxonomy = 'tipo_experiencia';SQLEsperado:
%%PB_PROTECTED_24%%
0SQLVocê pode repetir o inventário:
%%PB_PROTECTED_25%%
SELECT
post_type,
COUNT(*) AS total
FROM wp_posts
GROUP BY post_type
ORDER BY post_type;SQLO CPT antigo não deve mais aparecer.
ETAPA 11: Limpar revisões
Se o site é novo/staging e você não precisa preservar o histórico de edição:
%%PB_PROTECTED_26%%
DELETE FROM wp_posts
WHERE post_type = 'revision';SQLIsso remove as revisões existentes.
Depois disso, pode haver postmeta ligado às revisões que acabou de remover.
É aí que entra a limpeza de órfãos.
ETAPA 12: Limpeza de resíduos das tabelas
Aqui entram as seis verificações/limpezas que usamos.
12.1 Postmeta órfão
Primeiro, se quiser apenas conferir:
%%PB_PROTECTED_27%%
SELECT COUNT(*) AS total
FROM wp_postmeta pm
LEFT JOIN wp_posts p
ON p.ID = pm.post_id
WHERE p.ID IS NULL;SQLPara apagar:
%%PB_PROTECTED_28%%
DELETE pm
FROM wp_postmeta pm
LEFT JOIN wp_posts p
ON p.ID = pm.post_id
WHERE p.ID IS NULL;SQLEssa query é extremamente útil em bancos herdados.
No nosso caso, ela removeu:
%%PB_PROTECTED_29%%
837.950 linhasSQLIsso parece assustador, mas a condição:
%%PB_PROTECTED_30%%
WHERE p.ID IS NULLSQLsignifica que ela somente remove metas cujo post_id não possui mais um registro correspondente em wp_posts.
Ou seja: dados órfãos.
Depois confira:
%%PB_PROTECTED_31%%
SELECT COUNT(*) AS total
FROM wp_postmeta pm
LEFT JOIN wp_posts p
ON p.ID = pm.post_id
WHERE p.ID IS NULL;SQLEsperado:
%%PB_PROTECTED_32%%
0SQL12.2 Commentmeta órfão
%%PB_PROTECTED_33%%
DELETE cm
FROM wp_commentmeta cm
LEFT JOIN wp_comments c
ON c.comment_ID = cm.comment_id
WHERE c.comment_ID IS NULL;SQLIsso remove metadata cujo comentário pai já não existe.
12.3 Relacionamentos de taxonomia órfãos
%%PB_PROTECTED_34%%
DELETE tr
FROM wp_term_relationships tr
LEFT JOIN wp_posts p
ON p.ID = tr.object_id
WHERE p.ID IS NULL;SQLIsso remove relacionamentos associados a objetos/posts inexistentes.
12.4 Termmeta órfão
%%PB_PROTECTED_35%%
DELETE tm
FROM wp_termmeta tm
LEFT JOIN wp_terms t
ON t.term_id = tm.term_id
WHERE t.term_id IS NULL;SQL12.5 Term taxonomy órfão
Primeiro confira:
%%PB_PROTECTED_36%%
SELECT COUNT(*) AS total
FROM wp_term_taxonomy tt
LEFT JOIN wp_terms t
ON t.term_id = tt.term_id
WHERE t.term_id IS NULL;SQLSe houver registros e você validar que são realmente órfãos:
%%PB_PROTECTED_37%%
DELETE tt
FROM wp_term_taxonomy tt
LEFT JOIN wp_terms t
ON t.term_id = tt.term_id
WHERE t.term_id IS NULL;SQL12.6 Não apagar termos só porque possuem count=0
Isto é importante.
Não faça:
%%PB_PROTECTED_38%%
DELETE FROM wp_term_taxonomy
WHERE count = 0;SQLUm termo pode estar corretamente cadastrado e simplesmente não possuir posts naquele momento.
count = 0 não significa lixo.
ETAPA 13: Limpar transients
Transients são caches temporários do WordPress e plugins.
Em instalação normal, eles costumam ficar na wp_options; com object cache persistente, como Redis/Memcached, podem estar fora do banco.
Se você quer limpar todos:
%%PB_PROTECTED_39%%
DELETE FROM wp_options
WHERE option_name LIKE '\_transient\_%'
OR option_name LIKE '\_site\_transient\_%';SQLIsso remove:
%%PB_PROTECTED_40%%
_transient_...
_transient_timeout_...
_site_transient_...
_site_transient_timeout_...SQLEles podem reaparecer imediatamente conforme o site roda.
Isso é normal.
Se você tiver WP-CLI, há uma opção ainda melhor:
%%PB_PROTECTED_41%%
wp transient delete --allSQLE para site transients:
%%PB_PROTECTED_42%%
wp transient delete --all --networkSQLO próprio WP-CLI documenta --all para apagar todos os transients.
ETAPA 14: Conferir novamente os post types
Depois da limpeza:
%%PB_PROTECTED_43%%
SELECT
post_type,
COUNT(*) AS total
FROM wp_posts
GROUP BY post_type
ORDER BY post_type;SQLVocê deve perceber:
- CPT legado sumiu;
revisionsumiu se você as apagou;- conteúdos atuais permanecem.
ETAPA 15: Otimizar as tabelas
Excluir centenas de milhares de registros não significa necessariamente que o espaço físico seja imediatamente recuperado.
Por isso, depois da limpeza, rode:
%%PB_PROTECTED_44%%
OPTIMIZE TABLE wp_postmeta;SQLNo nosso caso, essa era particularmente importante porque houve uma remoção gigantesca de postmeta.
Depois otimize as principais:
%%PB_PROTECTED_45%%
OPTIMIZE TABLE
wp_posts,
wp_postmeta,
wp_options,
wp_comments,
wp_commentmeta,
wp_terms,
wp_termmeta,
wp_term_taxonomy,
wp_term_relationships;SQLSe tiver WP-CLI:
%%PB_PROTECTED_46%%
wp db optimizePHPEsse é o comando oficial do WP-CLI para otimizar o banco.
ETAPA 16: Medir o tamanho das tabelas
Depois de tudo, vale verificar onde está o peso real:
%%PB_PROTECTED_47%%
SELECT
table_name,
engine,
table_rows,
ROUND(data_length / 1024 / 1024, 2) AS data_mb,
ROUND(index_length / 1024 / 1024, 2) AS index_mb,
ROUND(data_free / 1024 / 1024, 2) AS overhead_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY (data_length + index_length) DESC;SQLIsso mostra:
%%PB_PROTECTED_48%%
tabela
quantidade aproximada de linhas
dados
índices
espaço livre/overheadSQLÉ uma boa query para executar antes e depois da manutenção.
Checklist rápido para repetir em outro WordPress
Se amanhã você pegar outro site herdado, siga esta ordem:
%%PB_PROTECTED_49%%
1. Backup completo do banco
2. Listar todos os post_type
↓
3. Identificar CPTs suspeitos
↓
4. Listar os posts desses CPTs
↓
5. Verificar postmeta/ACF associado
↓
6. Identificar taxonomias relacionadas
↓
7. Conferir os termos dessas taxonomias
↓
8. Registrar temporariamente CPT/taxonomia se já foram removidos
↓
9. Excluir posts com wp_delete_post($id, true)
↓
10. Excluir termos com wp_delete_term()
↓
11. Remover o código temporário
↓
12. Confirmar via SQL que CPT/taxonomia zeraram
↓
13. Apagar revisions, se desejado
↓
14. Limpar postmeta órfão
↓
15. Limpar commentmeta órfão
↓
16. Limpar term_relationships órfãos
↓
17. Limpar termmeta órfão
↓
18. Verificar/limpar term_taxonomy órfão
↓
19. Limpar transients
↓
20. OPTIMIZE TABLE
↓
21. Testar o siteSQLO que NÃO apagar automaticamente
Nunca use uma limpeza genérica do tipo:
%%PB_PROTECTED_50%%
DELETE FROM wp_posts
WHERE post_type NOT IN (...);SQLIsso pode remover estruturas internas importantes.
Também não recomendo:
%%PB_PROTECTED_51%%
DELETE FROM wp_postmeta
WHERE meta_key LIKE '_%';SQLporque muitas informações legítimas do WordPress, plugins, SEO e ACF utilizam metakeys começando por _.
E não apague:
%%PB_PROTECTED_52%%
acf-field
acf-field-group
acf-ui-options-pageSQLsó porque parecem registros técnicos. Eles são parte das definições do ACF.
Estratégia resumida
A maneira que usamos foi:
1. SQL identifica.
“O que ainda existe?””Qual CPT é legado?””Quais taxonomias ele usa?”
2. WordPress exclui.
%%PB_PROTECTED_53%%
wp_delete_post()
wp_delete_term()SQLAssim o core consegue limpar corretamente os dados relacionados.
3. SQL limpa o que ficou órfão.
Principalmente:
%%PB_PROTECTED_54%%
wp_postmeta
wp_commentmeta
wp_term_relationships
wp_termmeta
wp_term_taxonomySQL4. Transients são zerados.
5. OPTIMIZE recupera e reorganiza espaço.
Esse processo é bem adequado para banco herdado, staging transformado em produção ou projeto criado a partir de outro WordPress.
Minha jornada no mundo digital começou no início dos anos 2000, desenvolvendo sites e trocando serviços por benefícios em jogos de RPG online, durante os primeiros anos da popularização da internet no Brasil. Sou formado em Desenvolvimento de Sistemas, Administração de Empresas e pós-graduado em Marketing Digital, com especializações em desenvolvimento web e arquitetura da informação. No entanto, meu verdadeiro aprendizado veio da prática — nas empresas em que atuei, enfrentando desafios reais e construindo resultados sólidos. Ao longo da carreira, acumulei experiências que vão da base operacional à liderança estratégica: atuei como ajudante de comunicação, analista de audiência, especialista em SEO, desenvolvedor front e back-end, gestor de tráfego, CRM, consultor de BI e especialista em produtos Google. Essa vivência me moldou como um estrategista digital completo, com visão holística e foco em performance. Sou referência em transformação digital para líderes de diversos setores, em especial saúde, finanças e tecnologia. Unindo branding, aquisição, cultura de dados e desenvolvimento web, desenho estratégias integradas que impulsionam crescimento, inovação e diferenciação competitiva.
César Canteiro possui 72 conteúdos publicados na Pink and Brain. Leia mais.