MySQL: как проверить, является ли строка действительной DATE, TIME или DATETIME
Когда я пытаюсь поместить значение в поле DATE, которое является недопустимым, MySQL, похоже, использует 0000-00-00 вместо этого. Есть ли способ, которым я могу выполнить эту "проверку" без обновления поля DATE? И сделать это, например, с PHP?
Например, есть ли способ, которым я могу запросить сервер MySQL и спросить: "Эй, действительно ли это DATE, TIME или DATETIME?"
Или может быть, даже лучший способ сделать это?
Ответы
Ответ 1
Если вы выберете режим сервера для сервера MySQL, который не разрешает недопустимые значения даты, запрос, содержащий такое неправильное представление даты, приведет к ошибке вместо (молча) при допущении 0000-00-00
см. http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
например.
$pdo = new PDO('mysql:host=localhost;dbname=test', 'localonly', 'localonly');
$pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$pdo->exec('CREATE TEMPORARY TABLE foo (id int auto_increment, d datetime, primary key(id))');
$query = "INSERT INTO foo (d) VALUES ('2010-02-31 12:15:18')";
foreach( array('ALLOW_INVALID_DATES', 'STRICT_ALL_TABLES') as $mode ) {
echo $mode, ": "; flush();
$pdo->exec("SET SESSION sql_mode='$mode'");
$pdo->exec($query);
echo "Ok.\n";
}
печатает
ALLOW_INVALID_DATES: Ok.
STRICT_ALL_TABLES:
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[22007]: Invalid datetime format: 1292 Incorrect datetime value: '2010-02-31 12:15:18' for column 'd' at row 1' in [...]
Ответ 2
Вы можете проанализировать дату в соответствии с форматом, который хотите использовать, а затем вызвать checkdate, чтобы проверить, действительно ли это действительная дата. Убедитесь, что вы прочитали комментарии http://php.net/manual/en/function.checkdate.php.
Ответ 3
Я использую этот синтаксис в своем приложении, и он работает как шарм!
SELECT DATE('2013-09-31') AS valid;
Моя система - Win7x64 с PHP 5.5 и MySQL 5.6.16
Ответ 4
Вы можете просто использовать "константный" запрос, не используя временные таблицы и тестовые поля:
mysql> select day('2010-02-31 00:00:00');
+----------------------------+
| day('2010-02-31 00:00:00') |
+----------------------------+
| NULL |
+----------------------------+
Ответ 5
Так как в вопросе говорится MySQL, вот решение MySQL:
Очень сложно проверить, является ли поле датой из-за всех разных возможных форматов даты, которые необходимо будет учитывать. НО, если вы знаете, что форматы даты поля являются одним из следующих:
'yyyy-mm-dd'
'yyyy-mm-dd hh:mm:ss'
'yyyy-mm-dd whatever'
Этот код поможет вам:
SELECT count(*) FROM `table`
WHERE DATE(STR_TO_DATE(`column`, '%Y-%m-%d')) IS NOT NULL
AND `column` NOT REGEXP '^[0-9\.]+$'
В основном:
- первое условие говорит вам, является ли дата, но, к сожалению, не исключает номера (например:
DATE(STR_TO_DATE(**1**, '%Y-%m-%d')) = '2001-00-00'
- вторая гарантирует, что числа исключены, что оставляет вас только с датами, которые следуют форматам выше.
Если count(*)
- >0
, тогда это дата, если это 0
что-то еще.
Примечание
Этот метод работает для строк любого формата даты, если вы заранее знаете, в каком формате они следуют (что я знаю не всегда так, но все же полезно). Просто замените формат a priori
в запросе
Ответ 6
Используйте эту функцию в php для проверки допустимой даты:
function valid_date($date) {
return (preg_match("/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/", $date));
}
Или, как @VolkerK сказал, что если в базу данных введена недействительная дата, она будет сохранена как 0000-00-00, чтобы вы также могли:
SELECT * FROM table WHERE date_field > '0000-00-00'
Ответ 7
Простая функция PHP, которая будет проверять типы данных MySQL "date" и "datetime",
в следующем примере кода (chk_MySQL_Datetime). Он проверяет с помощью функции createFromFormat()
создайте объект Date для даты/даты, если это не удастся, а затем дату/дату
является недействительным. Если создается объект Date, форматированная строка, созданная с помощью
date_format() и по сравнению с датой/временем.
Если строки равны, дата/дата-время действительны.
Если они не совпадают, дата/дата-время недействительны. Это сравнение
необходимо, потому что createFromFormat() примет 2009/02/29 09:85 в качестве действительного дата-времени
но MySQL не будет.
Следующий код включает пример/тестовый пример, который показывает использование и проверку базы данных MySQL.
Чтобы использовать этот пример, вам придется заменить $database- > domysql_query() на вызов в базу данных.
Выход эхо-сигналов включается как комментарии сразу после выражений эха.
<?php
/**
* Returns false if MySQL date or datetime value would be stored as
* 0000-00-00 or 0000-00-00 00:00:00.
* @param string $fmt - createFromFormat format of MySQL datetime value
* to be tested. see http://www.php.net/manual/en/datetime.createfromformat.php
* @param string $datetime MySQL datetime value
* to be tested.
* @return DateTime object formatted according to $fmt or FALSE if not
* valid MySQL datetime value (would be inserted into the database
* 0000-00-00 00:00:00);
*/
function chk_MySQL_Datetime($fmt, $datetime) {
global $fmtdatetime; // for example only
$ckdate = DateTime::createFromFormat($fmt, $datetime);
if ($ckdate !== FALSE) {
$fmtdatetime = date_format($ckdate, $fmt);
if ($fmtdatetime != $datetime) {
$ckdate = FALSE;
}
}
return $ckdate;
}
/* Example/test of chk_MySQL_Datetime */
/**
* Creates table datetimetest if it doesn't exist
* and insert a record in primary index of type datetime
* then select record inserted and return result as a formated string.
* @global type $database - addressibility to database functions
* @global type $mysqlenum - MySql errornumber of last operation
* @global type $fmtdatetime - $datetime formatted by date_format
* @param type $datetime - datetime vale to be set
* @return string of what was inserted
*/
function insert_in_table($datetime) {
global $database, $mysqlenum, $fmtdatetime;
$sql = "CREATE TABLE IF NOT EXISTS `datetimetest` (
`MySQL_datetime` datetime NOT NULL COMMENT 'datetime created by MySQL',
`in_datetime` varchar(90) NOT NULL COMMENT 'date time string',
`fmtdatetime` varchar(90) NOT NULL COMMENT 'Output of createFromFormat',
PRIMARY KEY (`MySQL_datetime`)
) ;";
$result = $database->domysql_query($sql, $database->connection, true);
$sql = "DELETE FROM `datetimetest` WHERE MySQL_datetime='$datetime' OR `MySQL_datetime` = '0000-00-00 00:00:00'; ";
$result = $database->domysql_query($sql, $database->connection, false);
$sql = "INSERT INTO `datetimetest` (MySQL_datetime, in_datetime, fmtdatetime)
VALUES ('$datetime', '$datetime', '$fmtdatetime')";
$result = $database->domysql_query($sql, $database->connection, false);
$sql = "SELECT * FROM `datetimetest` WHERE MySQL_datetime='$datetime' OR `MySQL_datetime` = '0000-00-00 00:00:00'; ";
$result = $database->domysql_query($sql, $database->connection, false);
$contxa = mysql_fetch_assoc($result);
$ret = " Inserted datetime = " . $contxa['in_datetime'] . "," .
" MySQL stored " . $contxa['MySQL_datetime'] .
", fmtdatetime = " . $contxa['fmtdatetime'] . "<br>";
return $ret;
}
global $fmtdatetime;
echo('<br>');
$format = 'Y-m-d';
$datetime = '2009-02-15'; // valid date
$date = chk_MySQL_Datetime($format, $datetime);
echo "Format: $format; datetime is " . ($date ? 'valid' : 'invalid' ) . " Expected $datetime is " . $fmtdatetime . "<br>";
//echo output = Format: Y-m-d; datetime is valid Expected 2009-02-15 is 2009-02-15
$result = insert_in_table($datetime);
echo $result . "<br>";
//echo output = Inserted datetime = 2009-02-15, MySQL stored 2009-02-15 00:00:00, fmtdatetime = 2009-02-15
$datetime = '2009-02-29'; //invalid date
$date = chk_MySQL_Datetime($format, $datetime);
echo "Format: $format; datetime is " . ($date ? 'valid' : 'invalid' ) . " Expected $datetime is " . $fmtdatetime . "<br>";
//echo output = Format: Y-m-d; datetime is invalid Expected 2009-02-29 is 2009-03-01
$result = insert_in_table($datetime);
echo $result . "<br>";
//echo output = Inserted datetime = 2009-02-29, MySQL stored 0000-00-00 00:00:00, fmtdatetime = 2009-03-01
$format = 'Y-m-d H:i';
$datetime = '2009-02-15 14:20';
$date = chk_MySQL_Datetime($format, $datetime);
echo "Format: $format; datetime is " . ($date ? 'valid' : 'invalid' ) . " Expected $datetime is " . $fmtdatetime . "<br>";
//echo output = Format: Y-m-d H:i; datetime is valid Expected 2009-02-15 14:20 is 2009-02-15 14:20
$result = insert_in_table($datetime);
echo $result . "<br>";
//echo output = Inserted datetime = 2009-02-15 14:20, MySQL stored 2009-02-15 14:20:00, fmtdatetime = 2009-02-15 14:20
$datetime = '2009-02-15 14:63'; // invalid time
$date = chk_MySQL_Datetime($format, $datetime);
echo "Format: $format; datetime is " . ($date ? 'valid' : 'invalid' ) . " Expected $datetime is " . $fmtdatetime . "<br>";
//echo output = Format: Y-m-d H:i; datetime is invalid Expected 2009-02-15 14:63 is 2009-02-15 15:03
$result = insert_in_table($datetime);
echo $result . "<br>";
//echo output = Inserted datetime = 2009-02-15 14:63, MySQL stored 0000-00-00 00:00:00, fmtdatetime = 2009-02-15 15:03
$datetime = 'x-02-15 14:59'; // invalid time
$date = chk_MySQL_Datetime($format, $datetime);
echo "Format: $format; datetime is " . ($date ? 'valid' : 'invalid' ) . " Expected $datetime is " . $fmtdatetime . "<br>";
//echo output = Format: Y-m-d H:i; datetime is invalid Expected x-02-15 14:59 is 2009-02-15 15:03
$result = insert_in_table($datetime);
echo $result . "<br>";
//echo output = Inserted datetime = x-02-15 14:59, MySQL stored 0000-00-00 00:00:00, fmtdatetime = 2009-02-15 15:03
?>
Ответ 8
Возможно, вам нужно создать базу данных с движком BLACKHOLE
, прочитайте здесь. И проверьте исключение, вызванное PDOException.
$dbh = new PDO('mysql:host=localhost;dbname=test', 'username', '[email protected]!!!', array(PDO::ATTR_PERSISTENT => true));
// CREATE DATABASE test;
// CREATE TABLE test.foo ( bar DATETIME ) ENGINE=BLACKHOLE;
try {
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dbh->beginTransaction();
$dateTime = new DateTime();
$dateTime = $dateTime->format('c');
$dbh->exec("INSERT INTO foo (bar) VALUES ('".$dateTime."')");
$dbh->commit();
} catch (PDOException $e) {
$errorInfo = $dbh->errorInfo();
if ($e->getCode() === '22007'
and $dbh->errorCode() === '22007'
and $errorInfo[0] === '22007'
and preg_match('/^incorrect datetime/', strtolower($errorInfo[2])) === 1) {
throw new Exception($errorInfo[2], (int) $e->getCode());
}
}
Ответ 9
DATETIME - в SQL
SELECT your_unreliable_datetime_column, CAST(your_unreliable_datetime_column as DATETIME)
FROM some_cool_table
WHERE
CAST(your_unreliable_datetime_column as DATETIME) IS NOT NULL
AND
your_unreliable_datetime_column = CAST(your_unreliable_datetime_column as DATETIME)
Для этого нужно выбрать только допустимый DATETIME
s, а должен быть применим к DATE
и TIME
соответственно, но не проверен...
Я не нашел никакой документации по возвращаемому значению CAST()
при ошибке преобразования, но документация охватывает специальный случай - возможно fail - который возвращает NULL
:
Для преобразования строки даты с нулевым значением в дату CONVERT()
и CAST()
return NULL
и вывести предупреждение, когда режим NO_ZERO_DATE
SQL включен.
Кроме того, из моих тестов кажется, что CAST()
ведет себя одинаково в любом преобразовании отказа - возвращает NULL
и генерирует предупреждение.
Я действительно протестировал это на MariaDB 10.0.30
Ответ 10
SELECT WEEK(col) IS NOT NULL AS valid;
ИЛИ один из:
SELECT DAYNAME(col) IS NOT NULL AS valid;
SELECT TO_DAYS(col) IS NOT NULL AS valid;
SELECT TO_SECONDS(col) IS NOT NULL AS valid;
SELECT WEEKDAY(col) IS NOT NULL AS valid;
SELECT WEEKOFYEAR(col) IS NOT NULL AS valid;
SELECT YEARWEEK(col) IS NOT NULL AS valid;
Ответ 11
Я делаю это:
if(strtotime(trim($str)) {
// persist trim($str) in db.
}