繁体   English   中英

从所有表中的MySQL数据库中删除所有零日期

[英]Remove all zero dates from MySQL database across all Tables

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

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

编辑:

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

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

要更改存在值,您可以使用如下查询:

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 ;

在这里查看示例。

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

试图查询这个

UPDATE tablename SET date_column = NULL WHERE date_column = '0000-00-00';

并收到以下错误:

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

在没有''围绕的工作原理的情况下,结果是以下查询!

UPDATE tablename SET date_column = NULL WHERE date_column = 0000-00-00;

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

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

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

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

你有两个选择。

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

  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; 执行stmt;

写起来并不难。

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

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

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替换为您的数据库名称,然后执行所有更新即可。

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

改变你的表格

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'

前缀:您可能想要在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'; 

然后可以将该列表作为语句进行审查和执行。

希望有帮助!

由于这是用于迁移,我建议您只需将表包装在视图中,在导出数据时进行转换。 我使用了以下概念将数据从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的表(使用像桥这样 ),并且只需运行一个查询等;

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM