Lio*_*ith 5 mysql datatypes varchar date mysql-5.7
我在创建带有生日字段的用户表时犯了一个错误,我没有放置 DATE 数据类型,而是放置了一个 VARCHAR!
所以现在我的用户表看起来像这样:
CREATE TABLE IF NOT EXISTS users
(
id INT UNSIGNED NOT NULL,
birthdate VARCHAR (200) NOT NULL,
PRIMARY KEY (id)
) DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)
它填充如下(样本):
INSERT INTO users (id, birthdate) VALUES
(1,'1991-01-23'),
(2,'yyyy-01-23'),
(3,'1991-mm-23'),
(4,'1991-01-dd'),
(5,''),
(6,'1991-01-d3'),
(7,'1983-05-23'),
(8,'1991-0m-23'),
(9,'19yy-01-23'),
(10,'y991-01-23');
Run Code Online (Sandbox Code Playgroud)
现在我想将每个不正确的生日更新为 NULL,或者设置一个默认值,如 2020-01-01。在这里查看我的 sqlfiddle 。
为此,我使用了 dbfiddle.uk(请参阅此处),而不是 sqlfiddle.com - 服务器更多,并且可以更好地保持最新状态。
所以,我所做的如下(设置按照您的sqlfiddle):
CREATE TABLE IF NOT EXISTS users (
id int(6) unsigned NOT NULL,
birthdate varchar(200) NOT NULL,
PRIMARY KEY (id)
)DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)
填充:
INSERT INTO users (id, birthdate) VALUES
(1,'1991-01-23'),
(2,'yyyy-01-23'),
(3,'1991-mm-23'),
(4,'1991-01-dd'),
(5,''),
(6,'1991-01-d3'),
(7,'1983-05-23'),
(8,'1991-0m-23'),
(9,'19yy-01-23'),
(10,'y991-01-23');
Run Code Online (Sandbox Code Playgroud)
添加一列来保存有效值:
ALTER TABLE users ADD new_bdate DATE; -- add column to hold valid values
Run Code Online (Sandbox Code Playgroud)
请注意,新字段可为空。必须如此,除非您想输入一些默认值,例如 01/01/1900 或“0000-00-00”或“2020-01-01” - 我(强烈)建议不要这样做!当优化器计算出计划时,它会让优化器感到困惑,而NULL
当您的数据未知时,它是完全有效的!
您在评论中提到尝试使用“0000-00-00”作为默认值会失败。这是因为sql_mode
包含STRICT_TRANS_TABLES
- 这在 MySQL 5.7 中默认启用(请参阅此处的文档) -此处和此处进一步讨论(以及随附的链接和注释)。来自 MySQL 文档:
严格模式会影响服务器是否允许“0000-00-00”作为有效日期:如果未启用严格模式,则允许“0000-00-00”并且插入不会产生警告。如果启用严格模式,则不允许使用“0000-00-00”并且插入会产生错误,除非同时给出 IGNORE。对于 INSERT IGNORE 和 UPDATE IGNORE,允许使用“0000-00-00”并且插入会产生警告。
因此,现在我们更改birthdate
字段以接受NULL
s - 这在稍后“清理”完成时很重要:
ALTER TABLE users MODIFY birthdate VARCHAR (200) NULL;
-- make birthdate nullable
-- this is important for the STR_TO_DATE function.
Run Code Online (Sandbox Code Playgroud)
我还使原始birthdate
字段可为空。如果不这样做,那么第一个UPDATE
就会失败,如下所示TRANSACTION
:
START TRANSACTION;
UPDATE users SET birthdate = NULL
WHERE birthdate REGEXP '[a-zA-Z/]' OR birthdate = '';
UPDATE users SET new_bdate = birthdate WHERE birthdate IS NOT NULL;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
在一个事务中执行两个 DML 步骤/查询以避免步骤之间的任何更新非常重要 - 尽管我认为您可以在安静的时间执行此操作 - 或者您可以在更改期间锁定表。
A-Z
大写的意思相同。该/
字符将与斜杠匹配 - 可在日期中使用,但对 MySQL 日期无效。现在,我们清理;
ALTER TABLE users DROP COLUMN new_bdate;
Run Code Online (Sandbox Code Playgroud)
最后,我们检查我们的结果:
SELECT * FROM users;
Run Code Online (Sandbox Code Playgroud)
结果:
id birthdate
1 1991-01-23
2 NULL
3 NULL
4 NULL
5 NULL
6 NULL
7 1983-05-23
8 NULL
9 NULL
10 NULL
10 rows
Run Code Online (Sandbox Code Playgroud)
因此,现在我们有一列具有正确的数据类型和正确的值(这些是已知的)。
您的问题表明了绝不允许在应用程序中输入自由文本的普遍重要性。如果可能的话,用户应该有义务从下拉菜单中进行选择,并从一开始就确保 NOT NULL 约束!
此外,它还显示了从第一天起选择正确数据类型的重要性!您的数据库是保护数据的最后堡垒,因此请确保输入的任何内容从一开始就有效- 您将避免出现此类问题!
遵循OP的评论,特别是关于在字段中输入的日期VARCHAR
(即YYYY/mm/dd
分隔符/
而不是`-`` - @Akina的(优雅的)REGEXP答案可以修改如下(添加合适的日期后 - 请参阅此处的小提琴) 。
INSERT INTO `users` (`id`, `birthdate`) VALUES
(11, '1993/03/20'),
(12, '2000/09/25');
(13, '2015.06.30'),
(14, '2015_04_15');
Run Code Online (Sandbox Code Playgroud)
请注意使用斜杠 ( /
)、点 ( .
) 或下划线 ( _
) 字符作为不同日期子字段的分隔符。
SQL:
UPDATE users
SET DOB =
STR_TO_DATE
(
CONCAT
(
SUBSTRING(birthdate, 1, 4),
'-',
SUBSTRING(birthdate, 6, 2),
'-',
SUBSTRING(birthdate, 9, 2)
), '%Y-%m-%d'
)
WHERE birthdate REGEXP '[0-9]{4}.[0-9]{2}.[0-9]{2}';
Run Code Online (Sandbox Code Playgroud)
结果(为简洁起见进行了剪裁):
id birthdate DOB
...
...
10 y991-01-23 NULL
11 1993/03/20 1993-03-20
12 2000/09/25 2000-09-25
13 2015.06.30 2015-06-30
14 2015_04_15 2015-04-15
14 rows
Run Code Online (Sandbox Code Playgroud)
这对@Akina的答案略有修改 - 它在正则表达式中使用点('.') - 点是正则表达式元字符(或“特殊”字符),它是一个通配符,可以代表任何单个(即一个且唯一一个)字符。
因此,任何年份后跟任何单个字符,后跟任何月份,后跟任何单个字符,任何一天都将匹配 - 这将覆盖有效的 ISO 日期(使用连字符 ( -
) 或其他可能的分隔符,即下划线或文字点字符。日期)正则表达式有些简化 - 真实的日期可能要复杂得多!
从字符串中提取日期我经历了一段非常棘手的时间。我使用了 MySQL(非标准 -令人惊讶!)字符串连接运算符(加号 ( +
))符号,它开始添加(即以数字方式)年份数字和月份数字。同样的事情也发生在 MySQL 版本的(标准 SQL)双管道 ( ||
) 运算符上。只有当我发现这一点时,我才设法找到最终的工作CONCAT
解决方案!
正如我的最后一个链接所说,"ya gotta love MySQL
“——这不是我第一个产生的情感……这也是我在这个论坛上宣传 PostgreSQL 的另一个原因!
要将您的出生日期字段设置为默认值 2020-01-01(不建议...见上文),请使用 @Akina评论中的代码:
SET DOB =
CASE
WHEN birthdate REGEXP {pattern 1}
THEN {expression 1}
WHEN {pattern 2} THEN {expression 2} ... ELSE NULL END
Run Code Online (Sandbox Code Playgroud)
像这样:
UPDATE users
SET DOB =
CASE
WHEN birthdate REGEXP '[0-9]{4}.[0-9]{2}.[0-9]{2}'
THEN
STR_TO_DATE
(
CONCAT
(
SUBSTRING(birthdate, 1, 4),
'-',
SUBSTRING(birthdate, 6, 2),
'-',
SUBSTRING(birthdate, 9, 2)
), '%Y-%m-%d'
)
ELSE '2020-01-01'
END;
Run Code Online (Sandbox Code Playgroud)
请参阅此处的小提琴。+1 对于一个有趣的第一个问题,它看起来(看似)简单,但却让我思考 - 欢迎来到论坛!
归档时间: |
|
查看次数: |
545 次 |
最近记录: |