如何比较 MySQL 中格式为 dd.mm.yyyy 的日期 (varchar)?

How do I compare dates (varchar) formatted as dd.mm.yyyy in MySQL?

我希望能够 select MySQL 数据库中两个日期之间的所有日期,其中日期是格式为 dd.mm.yyyy.[=16= 的 varchar ]

Database
date (varchar) | value
------------------------
31.01.2018     | 123
------------------------
28.02.2018     | 456

这行不通

Query
SELECT date,value from Database WHERE date BETWEEN '2018-01-01' AND '2018-01-31'

我知道我可以使用 WHERE date LIKE '%.01.2018' 整整几个月,但这还不够。

我可以在 PHP 中执行此操作,但不想那样做,因为那样会花费太长时间。 我想我可能会使用 CAST 函数,但不确定如何使用。

在 MySQL 中执行此操作的最佳方法是什么?

您可以使用 STR_TO_DATE():

WHERE STR_TO_DATE(date, '%d.%m.%Y') BETWEEN '2018-01-01' AND '2018-01-31'

你可以选择revo的方案,或者,这个方案

SELECT date,value from Database WHERE (not (date is null)) and CHAR_LENGTH(date) = 10 (CONCAT(SUBSTRING(date, 7), '-', SUBSTRING(date, 4, 2), '-', SUBSTRING(date, 1, 2))) BETWEEN '2018-01-01' AND '2018-01-31'

在这里,我们检查日期是否不为空且是否为所需的长度,因此您的程序在遇到非常规值时不会崩溃。

在 MySQL 中执行此操作的最佳方法是避免将日期存储为 DD.MM.YYYY 格式的字符串。相反,将日期存储在适当的 DATE 列中,以便它们匹配本机格式:YYYY-MM-DD。

使用STR_TO_DATE()或其他函数的解决方案意味着您不能使用索引来优化比较。搜索将 运行 性能不佳,因为它必须进行 table-扫描。


回复您的评论:

200 万行并不大 table。重组它的时间不应超过一两分钟。如果你等到 table 变大,它只会变得更难。

由于您的数据格式错误,您不能只更改数据类型。以下是我将如何解决这个问题:

第 1 步: 添加一个正确的 DATE 数据类型的新列:

ALTER TABLE MyTable ADD COLUMN myDate DATE;

(顺便说一下,不要命名您的 table "Database" 或您的专栏 "date"。这些是保留字,无论如何都不是描述性的。做你命名程序变量 "variable"?)

第 2 步: 创建触发器,以便您的应用对旧列的使用自动复制到新列:

CREATE TRIGGER InsMyDate BEFORE INSERT ON MyTable
FOR EACH ROW 
SET NEW.myDate = COALESCE(NEW.myDate, STR_TO_DATE(NEW.oldDate, '%d.%m.%Y'));

CREATE TRIGGER UpdMyDate BEFORE UPDATE ON MyTable
FOR EACH ROW 
SET NEW.myDate = COALESCE(NEW.myDate, STR_TO_DATE(NEW.oldDate, '%d.%m.%Y'));

COALESCE 的原因稍后就会清楚。

步骤 3: 回填过去行中的所有值:

UPDATE MyTable
SET myDate = STR_TO_DATE(NEW.oldDate, '%d.%m.%Y')
WHERE myDate IS NULL;

您只需执行一次。创建触发器后,插入的任何新行都会自动将正确的日期值复制到新列。在您创建触发器之前插入的任何行在新列中都将具有 NULL,因此您需要更新它们,如上所示。

第 4 步: 更改您的应用程序代码,以便对旧的错误日期列的任何引用都使用新的日期列。解决任何引用日期的插入、更新或 SELECT。测试并部署此代码更改。

执行此操作后,任何 INSERT 或 UPDATE 都将在新日期列中指定一个值,而不是 NULL。这将使触发器有效地成为空操作,因为 myDate 中的非 NULL 值将优先于 STR_TO_DATE() 表达式。这使您可以在方便的时候部署代码,数据库将继续对您的数据做正确的事情。

第 5 步: 删除触发器和旧的字符串日期列:

DROP TRIGGER InsMyDate;
DROP TRIGGER UpdMyDate;
ALTER TABLE MyTable DROP COLUMN oldDate;

如果你担心每个ALTER TABLE会在table-restructuring期间锁定table,你应该学会使用pt-online-schema-change。这允许您在不锁定的情况下执行 ALTER TABLE(重组结束时的短暂时刻除外)。