Réparer wp_options sans clé primaire ni unique key dans WordPress

Chez Codeable, j’ai travaillé sur l’optimisation d’un site e-commerce propulsé par WooCommerce récemment, qui connaissait quelques problèmes de lenteur.

La table wp_options est l’une des tables les plus sensibles de WordPress. Elle contient les réglages du site, les options des plugins, les transients, certains caches, les URLs principales, le thème actif, les plugins actifs et beaucoup de données chargées à chaque requête.

Quand sa structure est cassée, les symptômes peuvent être très variés : site lent, administration poussive, options dupliquées, erreurs dans phpMyAdmin, plugins qui réécrivent sans cesse les mêmes valeurs, ou outils de migration qui refusent de travailler proprement.

Un symptôme classique dans phpMyAdmin ou Adminer ressemble à ceci :

Current selection does not contain a unique column

Ce message signifie souvent que la table ne possède pas de colonne unique exploitable. Sur une table WordPress saine, wp_options doit pourtant avoir une clé primaire sur option_id, un AUTO_INCREMENT, une clé unique sur option_name, et un index sur autoload.

Voici comment diagnostiquer et réparer proprement une table wp_options à qui il manque une PRIMARY KEY, une contrainte UNIQUE, ou l’auto-incrémentation.

Distingo, le livret à 2%

Pourquoi la table wp_options est si importante

WordPress utilise wp_options en permanence. À chaque chargement, il lit plusieurs options essentielles, notamment siteurl, home, active_plugins, template, stylesheet, les règles de réécriture et les options autoloadées.

Cette table n’est donc pas une simple zone de stockage annexe. C’est un carrefour central. Si sa structure est abîmée, tout le site peut ralentir ou se comporter de manière incohérente.

La structure standard ressemble à ceci :

option_id    bigint(20) unsigned NOT NULL auto_increment
option_name  varchar(191) NOT NULL default ''
option_value longtext NOT NULL
autoload     varchar(20) NOT NULL default 'yes'

PRIMARY KEY (option_id)
UNIQUE KEY option_name (option_name)
KEY autoload (autoload)Langage du code : PHP (php)

La clé primaire identifie chaque ligne. La clé unique empêche deux options de porter le même nom. L’index autoload aide WordPress à récupérer les options chargées automatiquement.

Les symptômes d’une table wp_options cassée

Vous pouvez soupçonner un problème de structure si vous observez l’un de ces signes :

  • phpMyAdmin affiche Current selection does not contain a unique column.
  • Adminer ne permet pas de modifier certaines lignes proprement.
  • Des outils de migration signalent une clé primaire manquante.
  • Des plugins recréent sans cesse les mêmes options.
  • La table contient plusieurs lignes avec le même option_name.
  • Le champ option_id contient des valeurs à 0 ou des doublons.
  • Les inserts échouent avec une erreur de type Duplicate entry.
  • WordPress semble lent malgré un cache page correct.

Ce problème apparaît souvent après une migration ratée, un import SQL incomplet, une réparation MySQL hasardeuse, un changement de moteur de table, un vieux dump réimporté, ou une manipulation manuelle dans phpMyAdmin. Bref, les grands classiques du “j’ai juste cliqué vite fait”.

Distingo, le livret à 2%

Sauvegarder avant toute correction

Avant de toucher à wp_options, exportez la base. Ce n’est pas optionnel. Cette table contient trop de données critiques pour improviser.

wp db export before-wp-options-repair.sqlLangage du code : JavaScript (javascript)

Si vous êtes connecté en root :

wp db export before-wp-options-repair.sql --allow-rootLangage du code : JavaScript (javascript)

Vérifiez que le fichier existe et qu’il n’est pas vide :

ls -lh before-wp-options-repair.sqlLangage du code : CSS (css)

Sur un site WooCommerce ou un site à fort trafic, faites cette réparation sur staging. Ensuite seulement, reproduisez la procédure en production pendant une fenêtre de maintenance.

Vérifier la structure actuelle de wp_options

Commencez par afficher le préfixe de table WordPress :

wp db prefix

Puis décrivez la table des options :

wp db query "DESCRIBE $(wp db prefix)options;"Langage du code : JavaScript (javascript)

Vous pouvez aussi afficher les index existants :

wp db query "SHOW INDEX FROM $(wp db prefix)options;"Langage du code : JavaScript (javascript)

Une structure saine doit montrer :

  • option_id avec PRI et auto_increment ;
  • option_name avec un index unique ;
  • autoload avec un index simple.

Si option_id n’a pas PRI, si auto_increment manque, ou si option_name n’est pas unique, il faut corriger la structure.

Comparer avec le schéma attendu de WordPress

Voici le schéma attendu pour une table wp_options moderne :

CREATE TABLE wp_options (
  option_id bigint(20) unsigned NOT NULL auto_increment,
  option_name varchar(191) NOT NULL default '',
  option_value longtext NOT NULL,
  autoload varchar(20) NOT NULL default 'yes',
  PRIMARY KEY  (option_id),
  UNIQUE KEY option_name (option_name),
  KEY autoload (autoload)
);Langage du code : PHP (php)

Le préfixe peut évidemment être différent : wp_, wp_6tt9k634w0_, ou autre. Ne copiez jamais une requête avec wp_options en dur si votre site utilise un préfixe personnalisé.

Vérifier les doublons dans option_id

Avant d’ajouter une clé primaire, vérifiez que option_id ne contient pas de doublons :

wp db query "
SELECT option_id, COUNT(*) AS total
FROM $(wp db prefix)options
GROUP BY option_id
HAVING total > 1
ORDER BY total DESC, option_id ASC;
"Langage du code : PHP (php)

Vérifiez aussi les valeurs à zéro :

wp db query "
SELECT option_id, option_name, autoload
FROM $(wp db prefix)options
WHERE option_id = 0
ORDER BY option_name ASC;
"Langage du code : PHP (php)

Si vous trouvez plusieurs lignes avec option_id = 0, MySQL ne pourra pas ajouter une clé primaire. Il faut d’abord attribuer des identifiants uniques.

Kinsta: Premium Managed WordPress hosting

Corriger les option_id à zéro ou dupliqués

Si seule une poignée de lignes pose problème, je préfère corriger manuellement après inspection. Listez les lignes concernées, puis vérifiez leur option_name et leur valeur.

Pour obtenir le plus grand identifiant actuel :

wp db query "
SELECT MAX(option_id) AS max_option_id
FROM $(wp db prefix)options;
"Langage du code : PHP (php)

Ensuite, vous pouvez attribuer un nouvel identifiant à une ligne précise. Exemple, si une ligne problématique possède option_name = 'example_option' :

wp db query "
UPDATE $(wp db prefix)options
SET option_id = 123456
WHERE option_name = 'example_option'
LIMIT 1;
"Langage du code : PHP (php)

Remplacez 123456 par une valeur supérieure au maximum existant. Ce genre de correction doit rester ciblé. Si vous avez des milliers de lignes dupliquées, cherchez d’abord la cause : import cassé, restauration incomplète, plugin qui écrit mal, ou table sans auto-incrément depuis longtemps.

Ajouter la clé primaire et AUTO_INCREMENT

Une fois les doublons option_id corrigés, vous pouvez remettre le type attendu et la clé primaire :

wp db query "
ALTER TABLE $(wp db prefix)options
MODIFY option_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
ADD PRIMARY KEY (option_id);
"Langage du code : PHP (php)

Si une clé primaire existe déjà mais que AUTO_INCREMENT manque, utilisez seulement :

wp db query "
ALTER TABLE $(wp db prefix)options
MODIFY option_id bigint(20) unsigned NOT NULL AUTO_INCREMENT;
"Langage du code : PHP (php)

Évitez de passer option_id en INT. WordPress utilise bigint(20) unsigned pour ses identifiants dans les tables principales. Autant rester aligné sur le schéma du cœur.

Vérifiez ensuite :

wp db query "DESCRIBE $(wp db prefix)options;"Langage du code : JavaScript (javascript)

Vérifier les doublons dans option_name

Avant d’ajouter la clé unique sur option_name, il faut vérifier les doublons. C’est le point le plus délicat.

wp db query "
SELECT option_name, COUNT(*) AS total
FROM $(wp db prefix)options
GROUP BY option_name
HAVING total > 1
ORDER BY total DESC, option_name ASC;
"Langage du code : PHP (php)

Si la requête ne retourne rien, vous pouvez ajouter l’index unique. Si elle retourne des lignes, n’ajoutez pas encore la contrainte. MySQL refusera, et il aura raison.

Exemple d’erreur possible :

ERROR 1062 (23000): Duplicate entry 'jetpack_available_modules' for key 'option_name'Langage du code : JavaScript (javascript)

Cette erreur signifie que plusieurs lignes portent le même option_name. Or WordPress attend un nom d’option unique.

Inspecter les options dupliquées avant suppression

Pour inspecter une option dupliquée précise :

wp db query "
SELECT option_id, option_name, autoload, LENGTH(option_value) AS value_size
FROM $(wp db prefix)options
WHERE option_name = 'jetpack_available_modules'
ORDER BY option_id ASC;
"Langage du code : PHP (php)

Remplacez jetpack_available_modules par le nom réellement dupliqué.

Ensuite, décidez quelle ligne conserver. En général :

  • gardez la plus récente si l’option représente un état actuel ;
  • gardez la plus ancienne si les doublons viennent d’une duplication accidentelle récente ;
  • supprimez toutes les lignes si l’option appartient clairement à un plugin désinstallé ;
  • ne touchez pas à l’option si vous ne savez pas à quoi elle sert.

Le piège, c’est de supprimer automatiquement des doublons sans comprendre leur contenu. Sur un site WooCommerce ou avec des plugins complexes, une option peut contenir un tableau sérialisé important. La base n’est pas un terrain de paintball.

Supprimer les doublons en gardant la ligne la plus récente

Si vous avez vérifié les doublons et que vous voulez garder la ligne la plus récente pour chaque option_name, commencez par afficher ce qui serait supprimé :

wp db query "
SELECT o.option_id, o.option_name, o.autoload, LENGTH(o.option_value) AS value_size
FROM $(wp db prefix)options o
JOIN (
    SELECT option_name, MAX(option_id) AS keep_id
    FROM $(wp db prefix)options
    GROUP BY option_name
    HAVING COUNT(*) > 1
) d ON d.option_name = o.option_name
WHERE o.option_id <> d.keep_id
ORDER BY o.option_name, o.option_id;
"Langage du code : HTML, XML (xml)

Si le résultat est cohérent, exportez les lignes à supprimer avant effacement :

wp db query "
SELECT o.*
FROM $(wp db prefix)options o
JOIN (
    SELECT option_name, MAX(option_id) AS keep_id
    FROM $(wp db prefix)options
    GROUP BY option_name
    HAVING COUNT(*) > 1
) d ON d.option_name = o.option_name
WHERE o.option_id <> d.keep_id
ORDER BY o.option_name, o.option_id;
" > duplicated-options-before-delete.txtLangage du code : HTML, XML (xml)

Puis supprimez les doublons les plus anciens :

wp db query "
DELETE o
FROM $(wp db prefix)options o
JOIN (
    SELECT option_name, MAX(option_id) AS keep_id
    FROM $(wp db prefix)options
    GROUP BY option_name
    HAVING COUNT(*) > 1
) d ON d.option_name = o.option_name
WHERE o.option_id <> d.keep_id;
"Langage du code : HTML, XML (xml)

Cette méthode garde la ligne avec le plus grand option_id. Ce n’est pas toujours la bonne décision, mais c’est souvent la plus logique quand un plugin a recréé une option plusieurs fois et que la dernière valeur représente l’état le plus récent.

Supprimer les doublons en gardant la ligne la plus ancienne

Si vous voulez plutôt garder la ligne la plus ancienne, inspectez d’abord ce qui serait supprimé :

wp db query "
SELECT o.option_id, o.option_name, o.autoload, LENGTH(o.option_value) AS value_size
FROM $(wp db prefix)options o
JOIN (
    SELECT option_name, MIN(option_id) AS keep_id
    FROM $(wp db prefix)options
    GROUP BY option_name
    HAVING COUNT(*) > 1
) d ON d.option_name = o.option_name
WHERE o.option_id <> d.keep_id
ORDER BY o.option_name, o.option_id;
"Langage du code : HTML, XML (xml)

Puis supprimez les doublons plus récents :

wp db query "
DELETE o
FROM $(wp db prefix)options o
JOIN (
    SELECT option_name, MIN(option_id) AS keep_id
    FROM $(wp db prefix)options
    GROUP BY option_name
    HAVING COUNT(*) > 1
) d ON d.option_name = o.option_name
WHERE o.option_id <> d.keep_id;
"Langage du code : HTML, XML (xml)

Je l’utiliserais plutôt si les doublons sont apparus après une migration ou une restauration récente, et si les anciennes lignes correspondent clairement aux valeurs correctes.

Ajouter la clé unique sur option_name

Quand il n’y a plus de doublons dans option_name, ajoutez la contrainte unique :

wp db query "
ALTER TABLE $(wp db prefix)options
MODIFY option_name varchar(191) NOT NULL DEFAULT '',
ADD UNIQUE KEY option_name (option_name);
"Langage du code : PHP (php)

Si l’index existe déjà sous un autre nom, MySQL peut refuser la requête. Dans ce cas, inspectez d’abord les index :

wp db query "SHOW INDEX FROM $(wp db prefix)options;"Langage du code : JavaScript (javascript)

Ne créez pas deux index équivalents sur la même colonne. Cela ne rendra pas WordPress deux fois plus rapide. Cela rendra surtout la table plus lourde à maintenir.

Ajouter l’index sur autoload

Le schéma moderne de WordPress prévoit aussi un index sur autoload. Vérifiez s’il existe :

wp db query "
SHOW INDEX FROM $(wp db prefix)options
WHERE Column_name = 'autoload';
"Langage du code : PHP (php)

S’il manque, ajoutez-le :

wp db query "
ALTER TABLE $(wp db prefix)options
ADD KEY autoload (autoload);
"Langage du code : PHP (php)

Cet index ne remplace pas un nettoyage des options autoloadées. Il aide seulement MySQL à chercher les lignes concernées. Si vous avez 20 Mo d’options chargées automatiquement, l’index ne transformera pas une enclume en plume.

Vérifier la structure finale

Après correction, vérifiez la structure :

wp db query "DESCRIBE $(wp db prefix)options;"Langage du code : JavaScript (javascript)

Puis les index :

wp db query "SHOW INDEX FROM $(wp db prefix)options;"Langage du code : JavaScript (javascript)

Vous devez retrouver l’équivalent de ceci :

option_id    PRI    auto_increment
option_name  UNI
autoload     index

Ensuite, testez WordPress :

  • page d’accueil ;
  • administration ;
  • page Extensions ;
  • mise à jour d’une option simple ;
  • connexion et déconnexion ;
  • panier et checkout si WooCommerce est actif ;
  • tâches cron ou actions planifiées si le site en dépend.

Optimiser la table après réparation

Une fois les clés réparées, vous pouvez optimiser les tables :

wp db optimize

Si le site utilise encore MyISAM, envisagez une conversion propre vers InnoDB. J’ai détaillé la procédure ici : convertir les tables WordPress de MyISAM à InnoDB avec WP-CLI.

Surveiller les options autoloadées

Réparer les clés ne suffit pas toujours à régler les lenteurs. La table peut aussi être trop grosse, ou contenir trop d’options autoloadées.

Pour afficher les plus grosses options chargées automatiquement :

wp db query "
SELECT option_name, autoload, LENGTH(option_value) AS size
FROM $(wp db prefix)options
WHERE autoload = 'yes'
ORDER BY size DESC
LIMIT 30;
"Langage du code : PHP (php)

Si vous trouvez de très grosses options liées à des plugins désinstallés, ne les supprimez pas immédiatement. Identifiez d’abord leur origine. J’ai détaillé cette méthode dans ce guide pour identifier les options de base de données liées aux plugins WordPress.

Pour un nettoyage plus global de la base, vous pouvez aussi lire WordPress : nettoyage de la base de données.

Pourquoi ce problème revient parfois

Une fois réparée, la structure ne devrait pas se casser toute seule. Si le problème revient, cherchez une cause plus profonde :

  • script d’import SQL qui recrée les tables sans index ;
  • outil de migration qui exporte mal les contraintes ;
  • restauration partielle depuis un dump ancien ;
  • réparation MySQL incomplète après crash ;
  • plugin qui écrit directement en base ;
  • manipulation manuelle dans phpMyAdmin ;
  • table convertie ou copiée sans clés ;
  • réplication ou synchronisation de base mal configurée.

La correction SQL répare la table. Elle ne corrige pas forcément la procédure qui l’a cassée. Si vous restaurez régulièrement des dumps, vérifiez que les clés et index sont bien inclus dans l’export.

Besoin d’aide pour réparer une base WordPress ?

Besoin d’un développeur WordPress pour réparer votre base de données ?

Si votre table wp_options contient des doublons, manque de clés, grossit anormalement ou ralentit tout le site, je peux vous aider à diagnostiquer le problème sans supprimer des données au hasard.

J’interviens comme développeur WordPress et WooCommerce pour auditer la structure des tables, réparer les clés primaires, corriger les index manquants, nettoyer les options inutiles et optimiser la base sans casser les réglages actifs.

  • Audit de wp_options, wp_postmeta et tables WooCommerce.
  • Correction des clés primaires, index, doublons et AUTO_INCREMENT.
  • Nettoyage des options autoloadées, transients et données orphelines.
  • Analyse des requêtes SQL lentes et des plugins gourmands.
  • Intervention sauvegardée, testée et documentée.

Vous voulez réparer la base sans jouer à la roulette russe dans phpMyAdmin ? Contactez-moi. Je vous dirai ce qui peut être corrigé, ce qui doit rester, et ce qui ralentit réellement votre site.

Checklist de réparation wp_options

  • Exporter la base avec wp db export.
  • Travailler sur staging si le site est critique.
  • Vérifier la structure avec DESCRIBE.
  • Afficher les index avec SHOW INDEX.
  • Vérifier les doublons dans option_id.
  • Corriger les option_id à zéro ou dupliqués.
  • Ajouter PRIMARY KEY et AUTO_INCREMENT sur option_id.
  • Vérifier les doublons dans option_name.
  • Inspecter les doublons avant suppression.
  • Ajouter UNIQUE KEY option_name.
  • Ajouter l’index autoload si nécessaire.
  • Optimiser la base avec wp db optimize.
  • Tester le front, le back-office et WooCommerce si actif.

FAQ : wp_options sans clé primaire ou unique key

Pourquoi phpMyAdmin affiche Current selection does not contain a unique column ?

Parce que la table ne possède pas de colonne unique utilisable pour identifier chaque ligne. Sur wp_options, cela indique souvent que la clé primaire sur option_id ou la clé unique sur option_name manque.

Quelle est la structure normale de wp_options ?

La table doit contenir option_id en bigint unsigned AUTO_INCREMENT PRIMARY KEY, option_name en varchar(191) avec une clé unique, option_value en longtext, et autoload avec un index.

Puis-je ajouter directement une clé unique sur option_name ?

Oui, mais seulement s’il n’existe aucun doublon dans option_name. Sinon MySQL refusera l’ajout de la contrainte avec une erreur Duplicate entry.

Faut-il garder le doublon le plus ancien ou le plus récent ?

Il n’y a pas de règle universelle. Gardez la valeur la plus récente si elle représente l’état actuel. Gardez l’ancienne si les doublons viennent d’une migration récente. Dans tous les cas, inspectez les données avant suppression.

Est-ce que réparer les index accélère WordPress ?

Oui, si les index manquaient réellement. Cependant, une table wp_options peut aussi ralentir le site à cause d’options autoloadées trop lourdes. Les clés corrigent la structure ; elles ne remplacent pas un audit de contenu.

Puis-je lancer ces requêtes sur un site WooCommerce en production ?

Je le déconseille sans staging ni sauvegarde. WooCommerce dépend fortement des options et des tâches planifiées. Testez d’abord, puis intervenez en production pendant une période calme.

Sources

Demandez à l'IA son opinion
Gravatar for Matt Biscay

Je suis Matt Biscay, développeur WordPress & WooCommerce certifié chez Codeable, administrateur système et enseignant.

J’aide les entreprises à créer, optimiser et fiabiliser leurs sites WordPress avec une approche technique propre : performance, sécurité, maintenance, développement sur mesure et résolution de problèmes complexes.

Sur Skyminds, je partage des tutoriels WordPress, WooCommerce, Linux et administration système, avec des solutions testées sur des cas réels et pensées pour durer.

Découvrez mes services WordPress et WooCommerce.

Opinions