【问题标题】:Remove all zero dates from MySQL database across all Tables从 MySQL 数据库中删除所有表中的所有零日期
【发布时间】:2014-04-30 10:21:32
【问题描述】:

我在 MySQL 中有很多表,其中包含 dateTime 列中的零日期 0000-00-00 00:00:00

使用某种管理设置,是否可以禁用零日期并将所有零替换为静态值,例如 1-1-1900

编辑:

我正在进行数据库迁移,其中涉及将 100 多个 MySQL 表迁移到 SQL Server。

我可以通过设置来避免在每个表上手动执行脚本吗 数据库模式?

【问题讨论】:

  • 我不熟悉 MySQL 中的数据库模式。你有参考吗?您绝对不需要手动进行更改!
  • 为什么没有选择正确的答案?

标签: mysql mysql-workbench


【解决方案1】:

要更改现有值,您可以使用如下查询:

UPDATE tablename SET date_column = '1900-01-01' WHERE date_column = '0000-00-00';

如果您想自动执行 UPDATE 查询,您可以使用准备好的语句:

SET @sql_update=CONCAT_WS(' ', 'UPDATE', CONCAT(_schema, '.', _table),
                               'SET', _column, '=', '\'1900-01-01\'',
                               'WHERE', _column, '=', '\'0000-00-00\'');

PREPARE stmt FROM @sql_update;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

您可以遍历当前架构上所有表中声明为日期的所有列:

SELECT
  table_schema,
  table_name,
  column_name
FROM
  information_schema.columns
WHERE
  table_schema=DATABASE() AND data_type LIKE 'date%'

要遍历所有列,您可以使用存储过程:

DELIMITER //
CREATE PROCEDURE update_all_tables() BEGIN
  DECLARE done BOOLEAN DEFAULT FALSE;
  DECLARE _schema VARCHAR(255);
  DECLARE _table VARCHAR(255);
  DECLARE _column VARCHAR(255);
  DECLARE cur CURSOR FOR SELECT
                           CONCAT('`', REPLACE(table_schema, '`', '``'), '`'),
                           CONCAT('`', REPLACE(table_name, '`', '``'), '`'),
                           CONCAT('`', REPLACE(column_name, '`', '``'), '`')
                         FROM
                           information_schema.columns
                         WHERE
                           table_schema=DATABASE() AND data_type LIKE 'date%';

  DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE;

  OPEN cur;

  columnsLoop: LOOP
    FETCH cur INTO _schema, _table, _column;
    IF done THEN
      LEAVE columnsLoop;
    END IF;   

    SET @sql_update=CONCAT_WS(' ', 'UPDATE', CONCAT(_schema, '.', _table),
                                   'SET', _column, '=', '\'1900-01-01\'',
                                   'WHERE', _column, '=', '\'0000-00-00\'');

    PREPARE stmt FROM @sql_update;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

  END LOOP columnsLoop;

  CLOSE cur;
END//
DELIMITER ;

请看一个例​​子here

【讨论】:

    【解决方案2】:

    这是一个老问题,但遇到了类似的问题,只是我试图将 0000-00-00 设置为 NULL。

    试图查询这个

    UPDATE 表名 SET date_column = NULL WHERE date_column = '0000-00-00';

    并收到以下错误:

    不正确的日期值:第 1 行的“date_column”列的“0000-00-00”

    结果是在 0000-00-00 周围没有 '' 的以下查询有效!

     UPDATE 表名 SET date_column = NULL WHERE date_column = 0000-00-00;

    【讨论】:

    • 这也适用于激活 SQL 模式 NO_ZERO_IN_DATE 的“新”5.7 函数 - 谢谢,这节省了我的时间!
    • 我遇到了同样的问题,这个语句有效:UPDATE tablename SET date_column = '1900-01-01' WHERE date_column = 0000-00-00;
    【解决方案3】:

    您可以更改运行该查询的现有值

    update your_table
    set date_column = '1900-01-01'
    where date_column = '0000-00-00'
    

    您可以将表的定义更改为特定的默认值或null 像这样

    ALTER TABLE your_table 
    CHANGE date_column date_column date NOT NULL DEFAULT '1900-01-01'
    

    【讨论】:

    • 谢谢,我应该更具体一些,请检查我的编辑。
    【解决方案4】:

    你有两个选择。

    选项一 - 使用您选择的编程语言(您甚至可以使用存储过程执行此操作):

    1. 遍历您的INFORMATION_SCHEMA,可能是COLUMNS,并构建一个查询来取回您需要影响的表,即

    -

    SELECT TABLE_NAME FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE COLUMN_NAME='date' AND TABLE_SCHEMA='<YOUR DB NAME>'
    

    甚至更好

    SELECT TABLE_NAME,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS 
    WHERE COLUMN_NAME in ('timestamp','date','datetime')
    AND TABLE_SCHEMA='<YOUR DB NAME>'
    
    1. 存储结果,然后循环遍历它们。每个循环,创建一个新查询。在 MySQL 中,这将是一个带有准备好的语句的存储过程,AKA:

    -

    @string = CONCAT("UPDATE ", @table_name, " SET ", @column_name, "='1-1-1900' WHERE ", @column_name, "=0000-00-00 00:00:00");
    

    PREPARE stmt FROM @string; 执行语句;

    写起来不会太难。

    选项二 - 另一个例子,虽然技术含量肯定更低,但可能同样有效。在执行mysqldump 之后并在执行导出之前,您可以在文件中进行简单的搜索替换。 Vim 或任何其他文本编辑器会非常熟练地做到这一点,并允许您将 0000-00-00 00:00:00 替换为 1-1-1900。因为您几乎肯定不会找到不希望被替换的情况,所以这对您来说可能是最简单的选择。直接扔了!

    【讨论】:

      【解决方案5】:

      在我看来,您可以以最简单的方式生成所有更新:

      select
      concat('UPDATE ',TABLE_NAME,' SET ',COLUMN_NAME,'=NULL WHERE ',COLUMN_NAME,'=0;')
      from information_schema.COLUMNS 
      where TABLE_SCHEMA = 'DATABASE_NAME' and DATA_TYPE in ('datetime', 'date', 'time');
      

      只需将DATABASE_NAME 替换为您的数据库名称,然后执行所有更新。

      【讨论】:

        【解决方案6】:

        改变你的表为

        ALTER TABLE `test_table`
          CHANGE COLUMN `created_dt` `created_dt` date NOT NULL DEFAULT '1900-01-01';
        

        但在更改表之前,您需要更新现有值,如 juergen d 所说

        update test_table
        set created_dt= '1900-01-01'
        where created_dt= '0000-00-00'
        

        【讨论】:

        • 在添加约束之前,我必须更新所有行,对吧?
        • 这不是一个约束定义。
        • @VigneshKumar:没有必要这样做。
        【解决方案7】:

        您可以通过过滤日期等于 0 的位置来update 您的表,并且您可以为该列定义一个默认值。

        【讨论】:

        • 你能提供一个简单的例子吗?这真的会改善答案!
        【解决方案8】:

        前缀:您可能想查看 DataWareHousing 中 ETL 的概念,有一些工具可以为您完成简单的转换,甚至是 Kettle/Pentaho 等开源工具。

        但是,当您使用任何能够编写 SQL 查询的编程语言时,这很容易。我在 perl 中做了一个例子,但是 php 或 java 也可以完成这项工作:

        #!/usr/bin/perl
        
        use strict;
        use warnings;
        use DBI;
        
        my $user='geheim';
        my $pass='secret';
        
        my $dbh = DBI->connect( "dbi:mysql:host=localhost:database=to_convert:port=3306", $user, $pass ) or die $DBI::errstr;
        
        # Prints out all the statements needed, might be checked before executed
        my @tables = @{ $dbh->selectall_arrayref("show tables") };
          foreach my $tableh ( @tables){
            my $tabname = $tableh->[0];
            my $sth=$dbh->prepare("explain $tabname");
            $sth->execute();
            while (my $colinfo = $sth->fetchrow_hashref){
              if ($colinfo->{'Type'} =~ /date/i && $colinfo->{'Null'} =~ /yes/i){
                print ("update \`$tabname\` set \`" . $colinfo->{'Field'} . "\` = '1990-01-01' where \`" . $colinfo->{'Field'} . "\` IS NULL; \n");
                print ("alter table \`$tabname\` change column \`" . $colinfo->{'Field'} . "\`  \`" . $colinfo->{'Field'} . "\` " . $colinfo->{'Type'} . " not null default '1990-01-01'; \n");
              }
            }
          }
        

        这不会改变任何东西,但是当数据库有类似的表时:

        localmysql [localhost]> explain dt;
        +-------+------+------+-----+---------+-------+
        | Field | Type | Null | Key | Default | Extra |
        +-------+------+------+-----+---------+-------+
        | a     | date | YES  |     | NULL    |       |
        +-------+------+------+-----+---------+-------+
        1 row in set (0.00 sec)
        
        localmysql [localhost]> explain tst
            -> ;
        +-------+----------+------+-----+---------+-------+
        | Field | Type     | Null | Key | Default | Extra |
        +-------+----------+------+-----+---------+-------+
        | id    | int(11)  | YES  |     | NULL    |       |
        | atime | datetime | YES  |     | NULL    |       |
        +-------+----------+------+-----+---------+-------+
        2 rows in set (0.00 sec)
        

        它产生语句:

        update `dt` set `a` = '1990-01-01' where `a` IS NULL; 
        alter table `dt` change column `a`  `a` date not null default '1990-01-01'; 
        update `tst` set `atime` = '1990-01-01' where `atime` IS NULL; 
        alter table `tst` change column `atime`  `atime` datetime not null default '1990-01-01'; 
        

        然后可以查看此列表并将其作为语句执行。

        希望有帮助!

        【讨论】:

          【解决方案9】:

          由于这是用于迁移,我建议您只需将表包装在视图中,这些视图会在您导出数据时进行转换。我使用了以下概念将数据从 MySQL 移动到具有相同问题的 postgress。

          每个表都应该由这样的东西代理;

          CREATE VIEW migration_mytable AS 
          SELECT field1, field2, 
              CASE field3
                   WHEN '0000-00-00 00:00:00' 
                   THEN '1900-01-01 00:00:00' 
                   ELSE field3
              END CASE AS field3
          FROM mytable;
          

          您应该能够编写一个脚本,从目录中为您生成这个,以防您需要处理大量表格。

          然后您应该能够将数据导入到您的 SqlServer 表中(使用像 this 这样的桥接器),并简单地运行类似的查询;

          INSERT INTO sqlserver.mytable SELECT * FROM mysql.migration_mytable;
          

          【讨论】:

            猜你喜欢
            • 2019-01-28
            • 1970-01-01
            • 2014-04-13
            • 1970-01-01
            • 1970-01-01
            • 2014-04-01
            • 1970-01-01
            • 2019-02-14
            相关资源
            最近更新 更多