MySQL carrega valores NULL a partir de dados CSV

167

Eu tenho um arquivo que pode conter de 3 a 4 colunas de valores numéricos que são separados por vírgula. Os campos vazios são definidos com exceção quando estão no final da linha:

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

A tabela a seguir foi criada no MySQL:

+ ------- + -------- + ------ + ----- + --------- + ------- +
| Campo Tipo | Nulo Chave Padrão | Extra |
+ ------- + -------- + ------ + ----- + --------- + ------- +
| um | int (1) | SIM | NULL |
| dois | int (1) | Sim | NULL |
| três | int (1) | SIM | NULL |
| quatro | int (1) | SIM | NULL |
| cinco | int (1) | SIM | NULL |
+ ------- + -------- + ------ + ----- + --------- + ------- +

Estou tentando carregar os dados usando o comando MySQL LOAD:

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

A tabela resultante:

+ ------ + ------ + ------- + ------ + ------ +
| um | dois | três | quatro | cinco |
+ ------ + ------ + ------- + ------ + ------ +
| 1 | 2 3 4 | 5
| 1 | 2 3 0 5
| 1 | 2 3 NULL NULL
+ ------ + ------ + ------- + ------ + ------ +

O problema está no fato de que quando um campo está vazio nos dados brutos e não é definido, o MySQL, por algum motivo, não usa o valor padrão das colunas (que é NULL) e usa zero. NULL é usado corretamente quando o campo está ausente.

Infelizmente, eu tenho que ser capaz de distinguir entre NULL e 0 nesta fase, para que qualquer ajuda seja apreciada.

Obrigado S.

editar

A saída de SHOW WARNINGS:

+ --------- + ------ + -------------------------------- ------------------------ +
| Nível | Código Mensagem |
+ --------- + ------ + -------------------------------- ------------------------ +
| Aviso | 1366 Valor inteiro incorreto: '' para a coluna 'quatro' na linha 2 |
| Aviso | 1261 Linha 3 não contém dados para todas as colunas |
| Aviso | 1261 Linha 3 não contém dados para todas as colunas |
+ --------- + ------ + -------------------------------- ------------------------ +
Spiros
fonte
Com alterações no esquema de dados, eu usaria o d6tstack, que alinha todas as colunas antes da execução LOAD DATA. Consulte a seção de exemplos do d6tstack SQL sobre alterações no esquema de dados.
Citynorman

Respostas:

193

Isso fará o que você quiser. Ele lê o quarto campo em uma variável local e, em seguida, define o valor real do campo como NULL, se a variável local acabar contendo uma sequência vazia:

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,'')
;

Se todos estiverem vazios, você os lerá em variáveis ​​e terá várias instruções SET, como esta:

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
fonte
Teoricamente, suponho - mas está tudo na memória, e apenas contém pequenas quantidades de dados por linha, então imagino que seria infinitesimal; mas você deve testá-lo se achar que pode ser um problema.
Duncan Lock
4
Eu realmente gosto desta resposta. Os usuários podem ver cadeias de caracteres vazias ''quando fazem o download de um CSV (usando IFNULL(Col,'')na SELECT INTO OUTFILEconsulta) para o Excel, mas, em seguida, os uploads os aceitam como nulos versus tendo que lidar com \No CSV. Obrigado!
chrisan
9
para datas que usei 'NULLIF (STR_TO_DATE (@ date1, "% d /% m /% Y"), "0000-00-00")' '
Joaquín L. Robles
1
Eu tenho um arquivo csv que contém zeros 0que devem ser convertidos em NULL(porque não é possível ter valor zero para os dados em questão) e também seqüências de caracteres vazias. Como garantir que zeros e cadeias vazias sejam convertidos em NULL?
Paul Rougieux 11/09/17
Se os valores nulos e cadeias vazias estão em colunas separadas, em seguida, basta fazer o descrito acima para as cadeias vazias, e algo como este para os zeros: nullif(@vone, 0).
Duncan Lock
136

O manual do MySQL diz:

Ao ler dados com LOAD DATA INFILE, as colunas vazias ou ausentes são atualizadas com ''. Se você deseja um valor NULL em uma coluna, use \ N no arquivo de dados. A palavra literal "NULL" também pode ser usada em algumas circunstâncias.

Então, você precisa substituir os espaços em branco por \ N assim:

1,2,3,4,5
1,2,3,\N,5
1,2,3
Janci
fonte
3
Obrigado pela dica - sou cético em editar os dados de fonte bruta, mas se essa for a única maneira de contornar isso, testarei.
quer
7
Entendo seu ceticismo, ninguém gosta de editar dados brutos, apenas não parece certo. No entanto, se você pensar um pouco, deve haver uma maneira de distinguir entre NULL e string vazia. Se as entradas em branco forem traduzidas para NULLs, você precisará de uma sequência especial para a sequência vazia. Seria bom ter uma maneira de dizer ao MySQL como tratar entradas em branco, algo como LOAD DATA INFILE '/tmp/testdata.txt' INTO TABLE moo TRATAR EM BRANCO COMO NULO ...
Janci
2
OK, mas se você tem Fields enclosed by: "é que "\N"de"name",\N,"stuff"
Jonathon
3
Posso verificar que, pelo menos para o "phpMyAdmin 3.5.5", nenhum estilo de \Né aceito como denotador NULL. Em vez disso NULL, use , como neste exemplo:"name","age",NULL,"other","stuff"
Jonathon
1
Temos o MySQL 5.5.46-0 + deb8u1. Eu tentei tanto NULL e \ N e only \ N funcionou para nós.
raphael75
6

O comportamento é diferente dependendo da configuração do banco de dados. No modo estrito, isso geraria um erro ou um aviso. A consulta a seguir pode ser usada para identificar a configuração do banco de dados.

mysql> show variables like 'sql_mode';
Dobi
fonte
Obrigado! Eu estava coçando a cabeça tentando entender por que importar um CSV com colunas vazias que eu havia importado com sucesso no servidor de produção ontem não estava funcionando na minha nova instalação local - essa foi a resposta no meu caso!
22716 Emma Burrows
3

Pré-processe seu CSV de entrada para substituir entradas em branco por \ N.

Tentativa em um regex: s / ,, /, \ n, / ge es /, $ /, \ N / g

Boa sorte.

Sam Goldman
fonte
1
Este regex parcialmente funciona, ele não resolve entradas em branco sequenciais, por exemplo ,,,, será, \ n ,, \ n, deve ser utilizável se você executá-lo duas vezes
Ievgen
1
Resumirá a resposta e o comentário anterior. A seguir, trabalhei para mim, na ordem: arquivo sed -i / s / ,, /, \ N / g '$, arquivo sed -i / s / ,, /, / g' $, sed -i / s N, arquivo $ / \ N / g '$,
Omar Khazamov 3/16/16
Eu gostaria de fazer isso, mas não estou claro como você está executando esse regex. Se você estiver usando o MySQL para executar isso no arquivo, esta seria a melhor solução. Mas você não diz e eu não quero gastar muito tempo pesquisando como fazer algo que pode não ser possível.
DonkeyKong
1

(variável1, @ variável2, ..) SET variável2 = nullif (@ variável2, '' ou '') >> você pode colocar qualquer condição

Disse
fonte
0

mostrar variáveis

Show variables like "`secure_file_priv`";

Nota: mantenha seu arquivo csv no local indicado pelo comando acima.

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

Nota: aqui a datecoluna ' ' possui alguns valores em branco no arquivo 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
fonte