MySQL charge les valeurs NULL à partir des données CSV

167

J'ai un fichier qui peut contenir de 3 à 4 colonnes de valeurs numériques séparées par des virgules. Les champs vides sont définis à l'exception lorsqu'ils se trouvent à la fin de la ligne:

1,2,3,4,5
1,2,3,,5
1,2,3

Le tableau suivant a été créé dans MySQL:

+ ------- + -------- + ------ + ----- + --------- + ------- +
| Champ | Type | Null | Clé | Par défaut | Extra |
+ ------- + -------- + ------ + ----- + --------- + ------- +
| un | int (1) | OUI | | NULL | |
| deux | int (1) | OUI | | NULL | |
| trois | int (1) | OUI | | NULL | |
| quatre | int (1) | OUI | | NULL | |
| cinq | int (1) | OUI | | NULL | |
+ ------- + -------- + ------ + ----- + --------- + ------- +

J'essaie de charger les données à l'aide de la commande MySQL LOAD:

LOAD DATA INFILE '/tmp/testdata.txt' INTO TABLE moo FIELDS 
TERMINATED BY "," LINES TERMINATED BY "\n";

Le tableau résultant:

+ ------ + ------ + ------- + ------ + ------ +
| un | deux | trois | quatre | cinq |
+ ------ + ------ + ------- + ------ + ------ +
| 1 | 2 | 3 | 4 | 5 |
| 1 | 2 | 3 | 0 | 5 |
| 1 | 2 | 3 | NULL | NULL |
+ ------ + ------ + ------- + ------ + ------ +

Le problème réside dans le fait que lorsqu'un champ est vide dans les données brutes et n'est pas défini, MySQL pour une raison quelconque n'utilise pas la valeur par défaut des colonnes (qui est NULL) et utilise zéro. NULL est utilisé correctement lorsque le champ est totalement absent.

Malheureusement, je dois être capable de faire la distinction entre NULL et 0 à ce stade, donc toute aide serait appréciée.

Merci S.

Éditer

La sortie de SHOW WARNINGS:

+ --------- + ------ + -------------------------------- ------------------------ +
| Niveau | Code | Message |
+ --------- + ------ + -------------------------------- ------------------------ +
| Avertissement | 1366 | Valeur entière incorrecte: «» pour la colonne «quatre» à la ligne 2 |
| Avertissement | 1261 | La ligne 3 ne contient pas de données pour toutes les colonnes |
| Avertissement | 1261 | La ligne 3 ne contient pas de données pour toutes les colonnes |
+ --------- + ------ + -------------------------------- ------------------------ +
Spiros
la source
Avec des changements de schéma de données comme celui-ci, j'utiliserais d6tstack qui aligne toutes les colonnes avant de courir LOAD DATA. Voir la section des exemples SQL de d6tstack sur les changements de schéma de données.
citynorman

Réponses:

193

Cela fera ce que vous voulez. Il lit le quatrième champ dans une variable locale, puis définit la valeur réelle du champ sur NULL, si la variable locale finit par contenir une chaîne vide:

LOAD DATA INFILE '/tmp/testdata.txt'
INTO TABLE moo
FIELDS TERMINATED BY ","
LINES TERMINATED BY "\n"
(one, two, three, @vfour, five)
SET four = NULLIF(@vfour,'')
;

S'ils sont tous éventuellement vides, vous les liriez tous dans des variables et auriez plusieurs instructions SET, comme ceci:

LOAD DATA INFILE '/tmp/testdata.txt'
INTO TABLE moo
FIELDS TERMINATED BY ","
LINES TERMINATED BY "\n"
(@vone, @vtwo, @vthree, @vfour, @vfive)
SET
one = NULLIF(@vone,''),
two = NULLIF(@vtwo,''),
three = NULLIF(@vthree,''),
four = NULLIF(@vfour,'')
;
Duncan Lock
la source
Théoriquement, je suppose - mais tout est en mémoire, et ne contient que de minuscules quantités de données par ligne, donc j'imaginerais que ce serait infinitésimal; mais vous devriez le tester si vous pensez que cela pourrait poser un problème.
Duncan Lock
4
J'aime vraiment cette réponse. Les utilisateurs peuvent voir des chaînes vides ''lorsqu'ils téléchargent un csv (en utilisant IFNULL(Col,'')dans la SELECT INTO OUTFILErequête) pour Excel, mais les téléchargements les acceptent ensuite comme null plutôt que d'avoir à traiter \Ndans le csv. Merci!
chrisan le
9
pour les dates où j'ai utilisé 'NULLIF (STR_TO_DATE (@ date1, "% d /% m /% Y"), "0000-00-00")'
Joaquín L. Robles
1
J'ai un fichier csv qui contient des zéros 0qui doivent être convertis NULL(car il n'est pas possible d'avoir une valeur zéro pour les données en question) et également des chaînes vides. Comment s'assurer que les zéros et les chaînes vides sont convertis en NULL?
Paul Rougieux
Si les valeurs nulles et les chaînes vides sont dans des colonnes séparées, alors il suffit de faire ci - dessus pour les chaînes vides, et quelque chose comme ça pour les zéros: nullif(@vone, 0).
Duncan Lock
136

Le manuel MySQL dit:

Lors de la lecture de données avec LOAD DATA INFILE, les colonnes vides ou manquantes sont mises à jour avec ''. Si vous voulez une valeur NULL dans une colonne, vous devez utiliser \ N dans le fichier de données. Le mot littéral «NULL» peut également être utilisé dans certaines circonstances.

Vous devez donc remplacer les blancs par \ N comme ceci:

1,2,3,4,5
1,2,3,\N,5
1,2,3
Janci
la source
3
Merci pour le conseil - je suis sceptique quant à la modification des données sources brutes, mais si c'est le seul moyen de contourner le problème, je vais l'essayer.
Spiros
7
Je comprends votre scepticisme, personne n'aime éditer les données brutes, cela ne semble pas juste. Cependant, si vous y réfléchissez pendant une minute, il doit y avoir un moyen de faire la distinction entre une chaîne NULL et une chaîne vide. Si les entrées vides sont traduites en NULL, vous aurez besoin d'une séquence spéciale pour la chaîne vide. Ce serait bien d'avoir un moyen de dire à MySQL comment traiter les entrées vides, quelque chose comme LOAD DATA INFILE '/tmp/testdata.txt' INTO TABLE moo TRAAT BLANKS AS NULL ...
Janci
2
OK, mais si vous avez Fields enclosed by: "c'est celui "\N"de"name",\N,"stuff"
Jonathon
3
Je peux vérifier qu'au moins pour "phpMyAdmin 3.5.5" aucun style de \Nn'est accepté comme dénotant NULL. Utilisez plutôt NULL, comme dans cet exemple:"name","age",NULL,"other","stuff"
Jonathon
1
Nous avons MySQL 5.5.46-0 + deb8u1. J'ai essayé à la fois NULL et \ N, et only \ N a fonctionné pour nous.
raphael75
6

Le comportement est différent selon la configuration de la base de données. En mode strict, cela lèverait une erreur ou un avertissement. La requête suivante peut être utilisée pour identifier la configuration de la base de données.

mysql> show variables like 'sql_mode';
Dobi
la source
Merci! J'étais en train de me gratter la tête en essayant de comprendre pourquoi l'importation d'un CSV avec des colonnes vides que j'avais importé avec succès sur le serveur de production hier ne fonctionnait pas sur ma toute nouvelle installation locale - c'était la réponse dans mon cas!
Emma Burrows
3

Prétraitez votre CSV d'entrée pour remplacer les entrées vides par \ N.

Tentative sur une expression régulière: s / ,, /, \ n, / g et s /, $ /, \ N / g

Bonne chance.

Sam Goldman
la source
1
Cette regex fonctionne partiellement, elle ne résout pas les entrées vides séquentielles, par exemple ,,,, sera, \ n ,, \ n, devrait être utilisable si vous l'exécutez deux fois
ievgen
1
Résumera la réponse et le commentaire précédent. La suite a fonctionné pour moi, dans l'ordre: sed -i 's / ,, /, \ N / g' $ fichier, sed -i 's / ,, /, / g' $ fichier, sed -i 's / \ N, $ / \ N / g '$ file,
Omar Khazamov
Je voudrais faire cela, mais je ne suis pas clair sur la façon dont vous exécutez cette expression régulière. Si vous utilisez MySQL pour l'exécuter sur le fichier, ce serait la meilleure solution. Mais vous ne dites pas et je ne veux pas passer beaucoup de temps à chercher sur Google comment faire quelque chose qui n'est peut-être pas possible.
DonkeyKong
1

(variable1, @ variable2, ..) SET variable2 = nullif (@ variable2, '' ou '') >> vous pouvez mettre n'importe quelle condition

M'a dit
la source
0

afficher les variables

Show variables like "`secure_file_priv`";

Remarque: conservez votre fichier csv à l'emplacement indiqué par la commande ci-dessus.

create table assessments (course_code varchar(5),batch_code varchar(7),id_assessment int, assessment_type varchar(10), date int , weight int);

Remarque: ici, la datecolonne « » contient des valeurs vides dans le fichier csv.

LOAD DATA INFILE 'C:/ProgramData/MySQL/MySQL Server 8.0/Uploads/assessments.csv' 
INTO TABLE assessments
FIELDS TERMINATED BY ',' 
OPTIONALLY ENCLOSED BY '' 
LINES TERMINATED BY '\n' 
IGNORE 1 ROWS 
(course_code,batch_code,id_assessment,assessment_type,@date,weight)
SET date = IF(@date = '', NULL, @date);
Nirmal Silwal
la source