Valor incorreto do datetime do MySQL: '0000-00-00 00:00:00'

155

Recentemente, assumi um projeto antigo, criado há 10 anos. Ele usa o MySQL 5.1.

Entre outras coisas, preciso alterar o conjunto de caracteres padrão de latin1 para utf8.

Como exemplo, tenho tabelas como esta:

  CREATE TABLE `users` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `first_name` varchar(45) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
    `last_name` varchar(45) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
    `username` varchar(127) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
    `email` varchar(127) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
    `pass` varchar(20) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
    `active` char(1) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL DEFAULT 'Y',
    `created` datetime NOT NULL,
    `last_login` datetime DEFAULT NULL,
    `author` varchar(1) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT 'N',
    `locked_at` datetime DEFAULT NULL,
    `created_at` datetime DEFAULT NULL,
    `updated_at` datetime DEFAULT NULL,
    `ripple_token` varchar(36) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
    `ripple_token_expires` datetime DEFAULT '2014-10-31 08:03:55',
    `authentication_token` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `index_users_on_reset_password_token` (`reset_password_token`),
    UNIQUE KEY `index_users_on_confirmation_token` (`confirmation_token`),
    UNIQUE KEY `index_users_on_unlock_token` (`unlock_token`),
    KEY `users_active` (`active`),
    KEY `users_username` (`username`),
    KEY `index_users_on_email` (`email`)
  ) ENGINE=InnoDB AUTO_INCREMENT=1677 DEFAULT CHARSET=utf8 CHECKSUM=1 DELAY_KEY_WRITE=1 ROW_FORMAT=DYNAMIC

Eu configurei meu próprio Mac para trabalhar nisso. Sem pensar muito, executei o "brew install mysql", que instalava o MySQL 5.7. Então, eu tenho alguns conflitos de versão.

Eu baixei uma cópia desse banco de dados e importei.

Se eu tentar executar uma consulta como esta:

  ALTER TABLE users MODIFY first_name varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci    NOT NULL  

Eu recebo este erro:

  ERROR 1292 (22007): Incorrect datetime value: '0000-00-00 00:00:00' for column 'created' at row 1

Eu pensei que eu poderia consertar isso com:

  ALTER TABLE users MODIFY created datetime  NULL DEFAULT '1970-01-01 00:00:00';
  Query OK, 0 rows affected (0.06 sec)
  Records: 0  Duplicates: 0  Warnings: 0

mas eu entendo:

  ALTER TABLE users MODIFY first_name varchar(45) CHARACTER SET utf8 COLLATE utf8_general_ci    NOT NULL ;
  ERROR 1292 (22007): Incorrect datetime value: '0000-00-00 00:00:00' for column 'created' at row 1

Preciso atualizar todos os valores?

lorm
fonte

Respostas:

12

Minha sugestão, se for o caso de a tabela estar vazia ou não ser muito grande, é exportar as instruções create como um arquivo .sql, reescreva-as como desejar. Faça o mesmo se você tiver dados existentes, por exemplo, exportar instruções de inserção (eu recomendo fazer isso em um arquivo separado como as instruções de criação). Por fim, solte a tabela e execute primeiro a instrução create e insira.

Você pode usar esse mysqldumpcomando, incluído na sua instalação do MySQL, ou também o MySQL Workbench, que é uma ferramenta gráfica gratuita que inclui também essa opção de uma maneira muito personalizável, sem precisar procurar opções de comando específicas.

Lucia Pasarin
fonte
Lucia Pasarin, eu gosto muito da sua ideia, mas os dados não serão truncados? Alguns dados UTF8 ocupam mais bytes que latin1? Se algo anteriormente se encaixava no varchar 255, talvez agora não? Talvez eu deva mudar todos os varchars para campos de "texto"?
22416 lorm
Sim você está certo. Isso pode acontecer, já que latin1 usa 1 byte por caractere, enquanto o utf8 usa no máximo 4 bytes por caractere (dependendo da versão do MySQL e do tipo de utf8 dev.mysql.com/doc/refman/5.5/en/charset-unicode -utf8.html ). Portanto, você não precisa necessariamente do tipo TEXT. Eu diria que x4 seus tamanhos existentes anteriormente devem funcionar.
Lucia Pasarin 22/02
É o equivalente ao banco de dados da reformatação do disco rígido quando você deseja excluir um arquivo. É seguro dizer que esta solução não é aceitável em um ambiente de produção.
Brandon
198

Não pude fazer isso:

UPDATE users SET created = NULL WHERE created = '0000-00-00 00:00:00'

(no MySQL 5.7.13).

Eu continuei recebendo o Incorrect datetime value: '0000-00-00 00:00:00' erro.

Estranhamente, isso funcionou: SELECT * FROM users WHERE created = '0000-00-00 00:00:00' . Não tenho idéia por que o primeiro falha e o último funciona ... talvez um bug do MySQL?

De qualquer forma, essa consulta UPDATE funcionou:

UPDATE users SET created = NULL WHERE CAST(created AS CHAR(20)) = '0000-00-00 00:00:00'
obe
fonte
13
Eu tive o mesmo problema, mas definindo a última parte a apenas 0 fixo para mim assim:UPDATE users SET created = NULL WHERE created = '0'
Brian Leishman
SELECT * FROM entityWHERE createdAt = "0000-00-00 00:00:00" funciona bem, mas com uma falha atualizada! Eu tive o mesmo problema. Corrija-o com a solução do @obe CAST (criado como CHAR (20)) ... acho que é um bug.
Chrysweel 9/08/16
44
Para mim, funcionou como UPDATE users SET created = NULL WHERE created=0(sem "em torno de zero)"
KIR
1
Para substituir uma data "0000-00-00" apenas sem registro de data e hora, usei CHAR (11)
D.Tate
1
Isso é pura genialidade. Por que essa não é a resposta aceita? Somente para datas sem registro de data e hora, o valor mínimo é 1000-01-01. Considere usar isso como o valor padrão para cada atributo de data que você deseja deixar em branco ou com o valor 0000-00-00.
Arvanitis Christos
155

Alterando o valor padrão para uma coluna com uma ALTER TABLEinstrução, por exemplo

 ALTER TABLE users MODIFY created datetime  NULL DEFAULT '1970-01-02'

... não altera nenhum valor que já esteja armazenado. O valor "padrão" se aplica às linhas inseridas e para as quais um valor não é fornecido para a coluna.


Quanto ao motivo pelo qual você está encontrando o erro, é provável que a sql_modeconfiguração da sua sessão inclua NO_ZERO_DATE.

Referência: http://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_no_zero_date

Quando você fez a "importação", as instruções SQL que fizeram o INSERT nessa tabela foram executadas em uma sessão que permitia zero datas.

Para ver a configuração sql_mode:

SHOW VARIABLES LIKE 'sql_mode' ;

-ou-

SELECT @@sql_mode ;

Na medida em que "consertar" o problema atual, para que o erro não seja gerado quando você executar o ALTER TABLE instrução.

Várias opções:

1) altere sql_modepara permitir zero datas, removendo NO_ZERO_DATEe NO_ZERO_IN_DATE. A alteração pode ser aplicada no arquivo my.cnf; portanto, após a reinicialização do MySQL Server,sql_mode variável será inicializada com a configuração em my.cnf.

Para uma mudança temporária, podemos modificar a configuração com uma única sessão, sem exigir uma mudança global.

-- save current setting of sql_mode
SET @old_sql_mode := @@sql_mode ;

-- derive a new value by removing NO_ZERO_DATE and NO_ZERO_IN_DATE
SET @new_sql_mode := @old_sql_mode ;
SET @new_sql_mode := TRIM(BOTH ',' FROM REPLACE(CONCAT(',',@new_sql_mode,','),',NO_ZERO_DATE,'  ,','));
SET @new_sql_mode := TRIM(BOTH ',' FROM REPLACE(CONCAT(',',@new_sql_mode,','),',NO_ZERO_IN_DATE,',','));
SET @@sql_mode := @new_sql_mode ;

-- perform the operation that errors due to "zero dates"

-- when we are done with required operations, we can revert back
-- to the original sql_mode setting, from the value we saved
SET @@sql_mode := @old_sql_mode ;

2) mude o created coluna para permitir valores NULL e atualize as linhas existentes para alterar as datas zero para valores nulos

3) atualize as linhas existentes para alterar as datas zero para uma data válida


Não precisamos executar instruções individuais para atualizar cada linha. Podemos atualizar todas as linhas de uma só vez (assumindo que seja uma tabela de tamanho razoável. Para uma tabela maior, para evitar a geração de desfazer / desfazer enormes, podemos executar a operação em pedaços de tamanho razoável.)

Na pergunta, o AUTO_INCREMENTvalor mostrado para a definição da tabela garante que o número de linhas não seja excessivo.

Se já alteramos a createdcoluna para permitir NULLvalores, podemos fazer algo assim:

UPDATE  `users` SET `created` = NULL WHERE `created` = '0000-00-00 00:00:00'

Ou, podemos configurá-los para uma data válida, por exemplo, 2 de janeiro de 1970

UPDATE  `users` SET `created` = '1970-01-02' WHERE `created` = '0000-00-00 00:00:00'

(Observe que um valor datetime da meia-noite de 1º de janeiro de 1970 ( '1970-01-01 00:00:00') é uma "data zero". Isso será avaliado como'0000-00-00 00:00:00'

spencer7593
fonte
3
Sim, isso funcionou para mim. Procurei o modo sql no arquivo my.ini e removi NO_ZERO_IN_DATE e NO_ZERO_DATE. depois reiniciou o serviço. obrigado spencer7593!
mili 29/01
Defina NO_ZERO_DATE: stackoverflow.com/questions/3891896/…
user 1007017
Quando # 2 "altera a coluna criada para permitir valores NULL", isso não me permite, porque os valores da coluna ainda produzem o erro. Bastante preso.
Mike Weir
1
@ MikeWeir: presumivelmente " não me permite " significa que um erro é retornado quando uma instrução SQL é executada. Provavelmente devido à configuração de sql_mode. Versões mais recentes do MySQL têm configurações padrão sql_modemais rigorosas que as versões anteriores. Referência para 8,0 aqui: dev.mysql.com/doc/refman/8.0/en/sql-mode.html ver NO_ZERO_DATE, ALLOW_INVALID_DATEet al. note que alguns estão incluídos no modo STRICT, por exemplo STRICT_TRANS_TABLES, STRICT_ALL_TABLESe outros modos de combinação. Para contornar as restrições, modificar temporariamente sql_mode para a sessão,
spencer7593
@ spencer7593 com certeza. Eu não senti que essa opção fosse a melhor. Eu segui seu conselho de definir um valor de data muito antigo (1970) e meu sistema simplesmente o ignorará. Obrigado por todos os seus detalhes.
Mike Weir
67

Eu o consertei fazendo isso antes da consulta

SET SQL_MODE='ALLOW_INVALID_DATES';
Tariq Khan
fonte
Esta é a única resposta. Usado como: --init-command = 'SET SESSION FOREIGN_KEY_CHECKS = 0; SET SQL_MODE =' ALLOW_INVALID_DATES ''
Konchog
Muito obrigado. Não posso explicar que dor esse problema tem sido para mim.
Dieter Gribnitz
42

De acordo com o Manual de Referência do MySQL 5.7 :

O modo SQL padrão no MySQL 5.7 inclui estes modos: ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER e NO_ENGINE_SUBSTITION.

Como 0000-00-00 00:00:00não é um DATETIMEvalor válido , seu banco de dados está quebrado. É por isso que o MySQL 5.7 - que vem com o NO_ZERO_DATEmodo ativado por padrão - gera um erro quando você tenta executar uma operação de gravação.

Você pode corrigir sua tabela atualizando todos os valores inválidos para qualquer outro válido, como NULL:

UPDATE users SET created = NULL WHERE created < '0000-01-01 00:00:00'

Além disso, para evitar esse problema, recomendo que você sempre defina a hora atual como valor padrão para seus createdcampos semelhantes, para que sejam preenchidos automaticamente INSERT. Apenas faça:

ALTER TABLE users
ALTER created SET DEFAULT CURRENT_TIMESTAMP
Ivan Filho
fonte
8
SET sql_mode = 'NO_ZERO_DATE';
UPDATE `news` SET `d_stop`='2038-01-01 00:00:00' WHERE `d_stop`='0000-00-00 00:00:00'
Andrew March
fonte
4
Essa resposta seria melhorada com a discussão de por que isso resolve o problema.
KevinO
Isso afeta o armazenamento de data e hora. ou causar problemas
Pergunte Bytes
8

Aqui está minha solução PhpMyAdmin / Fedora 29 / MySQL 8.0 (por exemplo):

set sql_mode='SOMETHING'; não funciona , chamada de comando bem-sucedida, mas nada foi alterado.

set GLOBAL sql_mode='SOMETHING'; alterar configuração global mudança permanente.

set SESSION sql_mode='SOMETHING'; alterar a configuração da sessão A variável SESSION afeta apenas o cliente atual.

https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html

Então eu faço isso:

  • Obtenha SQL_MODE: SHOW VARIABLES LIKE 'sql_mode';
  • Resultado: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
  • Remova no resultado: NO_ZERO_IN_DATE,NO_ZERO_DATE
  • Defina nova configuração: set GLOBAL SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'

Você pode remover ou adicionar outro modo da mesma maneira.

É útil mudar globalmente para usar e testar estruturas ou o modo sql_mode deve ser especificado em cada arquivo ou grupo de consultas.

Adaptado de uma pergunta, pergunte aqui: como-posso-desativar-mysql-strict-mode

Exemplo: instale o conteúdo mais recente do Joomla 4.0-alpha.

Edit: No PhpMyadmin, se você tiver o controle do servidor, poderá alterar o sql_mode(e todos os outros parâmetros) diretamente emPlus > Variables > sql_mode

Shim-Sao
fonte
5

Você pode alterar o tipo de criado campo de datetimepara varchar(255), em seguida, você pode definir (atualização) todos os registros que têm o valor "0000-00-00 00:00:00"para NULL.

Agora, você pode fazer suas consultas sem erros. Depois de terminar, você pode alterar o tipo do campo criado para datetime.

Abdallah Ajlouni
fonte
4

Eu também tenho esse erro depois de atualizar o MySQL de 5.6 para 5.7

Eu descobri que a melhor solução para mim era combinar algumas das soluções aqui e criar algo que funcionasse com o mínimo de entrada.

Eu uso o MyPHPAdmin pela simplicidade de enviar as consultas pela interface, porque então posso verificar a estrutura e tudo isso facilmente. Você pode usar o ssh diretamente ou alguma outra interface. O método deve ser semelhante ou mesmo assim.

...

1

Primeiro, verifique o erro real ao tentar reparar o banco de dados:

joomla.jos_menu Nota: As colunas TIME / TIMESTAMP / DATETIME do formato antigo foram atualizadas para o novo formato.

Aviso: Valor incorreto da data e hora: '0000-00-00 00:00:00' para a coluna 'selected_out_time' na linha 1

Erro: valor padrão inválido para 'selected_out_time'

status: Falha na operação

Isso indica que a coluna check_out_time na tabela jos_menu precisa ter todas as datas incorretas fixadas, assim como o "padrão" alterado.

...

2)

Eu executo a consulta SQL com base nas informações na mensagem de erro:

UPDATE jos_menu SET checked_out_time = '1970-01-01 08:00:00' WHERE checked_out_time = 0

Se você receber um erro, poderá usar a consulta abaixo que parece sempre funcionar:

UPDATE jos_menu SET checked_out_time = '1970-01-01 08:00:00' WHERE CAST(checked_out_time AS CHAR(20)) = '0000-00-00 00:00:00'

...

3)

Feito isso, execute a segunda consulta SQL:

ALTER TABLE `jos_menu` CHANGE `checked_out_time` `checked_out_time` DATETIME NULL DEFAULT CURRENT_TIMESTAMP;

Ou, no caso, é uma data que precisa ser NULL

ALTER TABLE `jos_menu` CHANGE `checked_out_time` `checked_out_time` DATETIME NULL DEFAULT NULL;

...

Se eu executar o banco de dados de reparo agora, recebo:

joomla.jos_menu OK

...

Funciona muito bem :)

Don King
fonte
4

Verifica

SELECT @@sql_mode;

se você vir o item 'ZERO_DATE', tente

SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE',''));   
SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'NO_ZERO_IN_DATE',''));   

Saia e entre novamente no seu cliente (isso é estranho) e tente novamente

commonpike
fonte
3

Tornar o modo sql não rigoroso

se estiver usando laravel, vá para config-> database, vá para mysql settings e torne o modo strict falso

Milind Chaudhary
fonte
Posso fazer isso em myphpadmin?
Don King
O phpMyAdmin é apenas uma interface para usar o servidor mysql atual, não importa qual interface você esteja usando comandos mysql não mudará com a interface. Tente este comando (defina sql_mode = '';) ou este (defina global sql_mode = '';) para desativá-lo.
Milind Chaudhary
1
sim eu encontrei todo o preciso para desligar o modo estrito está adicionando sql_mode = (e nada depois dela), pelo fundo do my.cnf
Don King
3

Eu tive um problema semelhante, mas no meu caso, alguma linha tinha o valor NULL.

então primeiro eu atualizo a tabela:

update `my_table`set modified = '1000-01-01 00:00:00' WHERE modified is null

problema resolvido, pelo menos no meu caso.

Lenon Tolfo
fonte
2

Eu também tenho

SQLSTATE [22007]: Formato de data e hora inválido: 1292 Valor incorreto de data e hora: '0000-00-00 00:00:00' para a coluna

informação de erro

Corrija isso alterando 0000-00-00 00:00:00 para 1970-01-01 08:00:00

1970-01-01 08:00:00 o carimbo de hora unix é 0

tekintian
fonte
1
o problema é OP não pode alterar a data devido a erro. Eu acho que é erro deNO_ZERO_DATE
GusDeCooL 11/17/17
2

Encontrei a solução em https://support.plesk.com/hc/en-us/articles/115000666509-How-to-change-the-SQL-mode-in-MySQL . Eu tive isso:

mysql> show variables like 'sql_mode';
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name | Value                                                                                                                                     |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| sql_mode      | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.01 sec)

Observe o NO_ZERO_IN_DATE,NO_ZERO_DATEresultado acima. Eu removi isso fazendo o seguinte:

mysql> SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected, 1 warning (0.00 sec)

Então eu tive isso:

mysql> show variables like 'sql_mode';
+---------------+--------------------------------------------------------------------------------------------------------------+
| Variable_name | Value                                                                                                        |
+---------------+--------------------------------------------------------------------------------------------------------------+
| sql_mode      | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+---------------+--------------------------------------------------------------------------------------------------------------+
1 row in set, 1 warning (0.01 sec)

Depois disso, eu poderia usar com ALTER TABLEêxito e alterar minhas tabelas.

Jaime Montoya
fonte
1

Foi isso que fiz para resolver meu problema. Eu testei no MySQL 5.7 local ubuntu 18.04.

set global sql_mode="NO_ENGINE_SUBSTITUTION";

Antes de executar esta consulta globalmente, adicionei um arquivo cnf no diretório /etc/mysql/conf.d . O nome do arquivo cnf é mysql.cnf e códigos

[mysqld]
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ALLOW_INVALID_DATES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

Então eu reinicio o mysql

sudo service mysql restart

Espero que isso possa ajudar alguém.

Kalyan Halder Raaz
fonte
Esta solução está funcionando até eu reiniciar o servidor MySQL. Mesmo após a reinicialização, o valor NO_ENGINE_SUBSTITUTIONestá em todas as colunas de, SELECT @@global.sql_mode, @@session.sql_mode, @@sql_mode;mas ainda assim recebo um erro pelo datetime inválido 0000-00-00 00:00:00até executar a primeira consulta novamente. set global sql_mode="NO_ENGINE_SUBSTITUTION";Alguma idéia?
Smamatti 27/10/19
A solução no meu caso foi remover NO_ZERO_IN_DATE,NO_ZERO_DATEna sua versão da linha my.ini que define o arquivo sql_mode.
Smamatti 27/10/19
1

Isso é incrivelmente feio, mas também corrigiu o problema rapidamente para mim. Sua tabela precisa de uma chave exclusiva que você usará para corrigir as colunas contaminadas. Neste exemplo, a chave primária é chamada 'id' e a coluna de carimbo de data / hora quebrada é chamada 'BadColumn'.

  1. Selecione os IDs das colunas contaminadas.

    select id from table where BadColumn='0000-00-00 00:00:00'

  2. Colete os IDs em uma sequência delimitada por vírgula. Exemplo: 1, 22, 33. Eu usei um invólucro externo para isso (um script Perl) para cuspir rapidamente todos eles.

  3. Use sua lista de IDs para atualizar as colunas antigas com uma data válida (1971 a 2038).

    update table set BadColumn='2000-01-01 00:00:00' where id in (1, 22, 33)

felwithe
fonte
1

Minha solução

SET sql_mode='';
UPDATE tnx_k2_items
SET created_by = 790
, modified = '0000-00-00 00:00:00'
, modified_by = 0
VietPublic
fonte
1

Ao invés de

UPDATE your_table SET your_column = new_valid_value where your_column = '0000-00-00 00:00:00';

Usar

UPDATE your_table SET your_column = new_valid_value where your_column = 0;
R2D3
fonte
0

Se você estiver inserindo os dados manualmente, considere remover os valores e os zeros no TIMESTAMP (6) .000000 para que ele se torne TIMESTAMP. Isso funcionou bem comigo.

Isaac
fonte