web-dev-qa-db-fra.com

Ajouter un indice unique échoue avec une erreur d'entrée en double, mais aucun duplicat n'a été trouvé

Table donné:

CREATE TABLE mytable (
  field_a CHAR(15) NOT NULL DEFAULT '',
  field_b MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
  field_c SMALLINT UNSIGNED NOT NULL DEFAULT 0,
  field_d SMALLINT UNSIGNED NOT NULL DEFAULT 0,
  field_e CHAR(1) NULL DEFAULT '',
  field_f SMALLINT UNSIGNED NOT NULL DEFAULT 0
) ENGINE=MyISAM DEFAULT CHARACTER SET=UTF8 COLLATE utf8_general_ci;

Index requis:

ALTER TABLE mytable ADD UNIQUE INDEX idx_key (field_a, field_b);

La taille des données est d'environ 51 mio. Lignes. Problème suivant:

Tentative 1: Si j'essaie de créer l'index après que les données sont dans le tableau, il échoue avec une erreur de clé en double. Un sélection sur la touche ayant échoué ne renvoie qu'une ligne (!).

 [23000][1062] Duplicate entry 'aaaaaaaaaaaaaaa-11111' for key 'idx_key'

 SELECT COUNT(*) FROM mytable WHERE field_a='aaaaaaaaaaaaaaa' AND field_b='11111'

retourne 1 (!)

Tentative 2: Si je crée l'index sur une table vide ou que je fais une clé privée combinée, puis mettez les données dans la table, le tableau ne contient que 27 de 51 Mio. Lignes (!).

Y a-t-il une sorte de limite sur l'index unique ou un bug?

J'utilise Mariadb 10.0.20. S'il vous plaît aider.

(( Mise à jour 1

Un nombre de lignes uniques

SELECT COUNT(DISTINCT field_a,field_b) from mytable; 

retourne 50 lignes Mio. Donc, il y a environ 1 mio doublons. Cela n'explique toutefois pas la 27 Mio de tentative 2 ou une mauvaise exception de la tentative 1.

(( Mise à jour 2

la table utilisée par la tentative 2:

CREATE TABLE mytable (
  field_a CHAR(15) NOT NULL DEFAULT '',
  field_b MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
  field_c SMALLINT UNSIGNED NOT NULL DEFAULT 0,
  field_d SMALLINT UNSIGNED NOT NULL DEFAULT 0,
  field_e CHAR(1) NULL DEFAULT '',
  field_f SMALLINT UNSIGNED NOT NULL DEFAULT 0,
  UNIQUE KEY idx_key (field_a, field_b)
) ENGINE=MyISAM DEFAULT CHARACTER SET=UTF8 COLLATE utf8_general_ci;

(( Mise à jour 3

Tentative 2 erreur résolue

Pour remplir la table, nous utilisons des instructions d'insertion avec plusieurs lignes de valeur dans chacune d'elles. Si une seule valeur de l'insert a violé la contrainte unique, toutes les autres valeurs où elles ne sont pas non plus insérées et ont causé 27 lignes Mio au lieu de 50 Mio.

APPROCHE 1: Toutefois, le bogue avec le mauvais message d'entrée en double reste toujours là même lorsque la base de données a été abandonnée et recréée, mais c'est un mal que je peux/doit vivre avec.

5
kromit

Il n'y a aucune garantie que la valeuraaaaaaaaaaaaaaa-11111 dans le message

[23000][1062] Duplicate entry 'aaaaaaaaaaaaaaa-11111' for key 'mykey'

est la valeur qui cause réellement la violation. Semble être un bug dans Mariadb et dans MySQL.

4
kromit

Plan A : Utilisez INSERT IGNORE Dans vos inserts par lots. De cette façon, les touches DUP ne causent pas de problèmes.

Plan B : Insérer dans une table avec INDEX, pas UNIQUE. Ensuite, vous pouvez enquêter sur les duplicats avant de décider de quoi faire avec eux.

Vous comprenez que CHAR(15) utf8 occupe 45 octets  TOUJOURS Pour la plupart ROW_FORMATs? Peut-être que VARCHAR(15) serait mieux? (S'il vous plaît ne citez pas le conte de femmes délabrées sur FIXED étant meilleur à Myisam.)

Ou peut-être que les données sont, disons, vieilles chaînes IPv4? Ils fonctionnent bien avec CHARACTER SET ascii - 15 octets pour CHAR(15) ou 1-16 octets pour VARCHAR(15). Ensuite, qu'en est-il de l'IPv6? Et sur l'incapacité de comparer les gammes?

2
Rick James