2010-04-22 65 views
18

Khi tôi cố gắng đặt một giá trị vào một lĩnh vực NGÀY đó là không hợp lệ, MySQL dường như sử dụng 0000-00-00 để thay thế. Có cách nào tôi có thể thực hiện việc "kiểm tra" này mà không cập nhật trường DATE không? Và để làm điều đó từ ví dụ PHP?MySQL: Làm thế nào để kiểm tra xem một chuỗi là một ngày hợp lệ, TIME hoặc DATETIME

Giống như, có cách nào tôi có thể truy vấn máy chủ MySQL và hỏi "Này, đây là DATE, TIME hoặc DATETIME có hợp lệ với bạn không?"

Hoặc có cách nào tốt hơn để làm điều đó không?

+0

Tôi thực sự ghét điều đó nếu bạn chèn bất kỳ giá trị không hợp lệ nào vào cột ngày trong mysq l thông qua php bạn nhận được một '0000-00-00'! Nó nên hoặc không (ưa thích), để trống hoặc đặt giá trị mặc định của trường. – Ray

+1

Tôi cũng đã làm, nhưng bạn nghĩ rằng bạn có thể thay đổi điều đó. Chỉ cần kích hoạt chế độ nghiêm ngặt hoặc một cái gì đó: http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html#sqlmode_allow_invalid_dates – Svish

+0

Cũng giống như trong câu trả lời của @VolkerK thực sự, http://stackoverflow.com/a/2690242/39321 :) – Svish

Trả lời

2

Nếu bạn chọn một chế độ máy chủ cho máy chủ MySQL mà không cho phép các giá trị ngày không hợp lệ một truy vấn có chứa một biểu diễn ngày bị thay đổi như vậy sẽ gây ra một lỗi thay vì (im lặng) giả 0000-00-00
thấy http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html

ví dụ

$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"; 
} 

in

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 [...] 
+0

Điều cần biết! Sẽ xem xét thiết lập chế độ đó. Với điều này tôi sẽ phải thực sự cố gắng và sau đó bắt ngoại lệ mặc dù. Có cách nào tôi có thể kiểm tra nó trước khi tôi chạy chèn hoặc cập nhật? – Svish

+0

Không ai mà tôi biết. Nhưng nó có vấn đề cho dù một (hư cấu) chức năng mysql \ _check \ _date() trả về false hoặc thực hiện truy vấn thất bại (với một mã lỗi cụ thể)? – VolkerK

+0

Khi tôi cần nó để xác thực đầu vào của người dùng, thì có, loại. Đặc biệt là nếu bạn muốn kiểm tra "phía máy khách" bằng AJAX hoặc một cái gì đó trước khi gửi đi. – Svish

0

Sử dụng chức năng này trong php để kiểm tra ngày hợp lệ:

function valid_date($date) { 
    return (preg_match("/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/", $date)); 
} 

Hoặc như @VolkerK nói nếu một ngày không hợp lệ được đưa vào cơ sở dữ liệu nó sẽ được lưu trữ là 0000-00-00 để bạn cũng có thể làm:

SELECT * FROM table WHERE date_field > '0000-00-00'

+1

nhưng hàm này trả về true với '2010-32-56' – osm

+0

Vâng, điều đó sẽ loại bỏ các ngày theo định dạng sai, nhưng không xử lý các ngày không hợp lệ. Đã sử dụng điều này để kiểm tra trước, ngoại trừ tôi sử dụng '\ d' thay vì' [0-9] '. Và dấu ngoặc đơn là không cần thiết AFAIK: '/^\ d {4} - \ d {2} - \ d {2} $ /' – Svish

6

Bạn có thể phân tích ngày theo định dạng bạn muốn sử dụng và sau đó gọi đến ngày kiểm tra để kiểm tra xem đó có phải là ngày hợp lệ hay không. Đảm bảo bạn đọc nhận xét trên http://php.net/manual/en/function.checkdate.php.

3

Bạn chỉ có thể sử dụng một truy vấn 'liên tục', mà không sử dụng bảng tạm thời và các lĩnh vực thử nghiệm:

mysql> select day('2010-02-31 00:00:00'); 
+----------------------------+ 
| day('2010-02-31 00:00:00') | 
+----------------------------+ 
|      NULL | 
+----------------------------+ 
+1

Thật kỳ quặc, điều này dường như không hoạt động. Tôi đã gặp một máy chủ có bản cập nhật cho lỗi 1292: 'Giá trị ngày giờ không chính xác: '2012-09-13T17: 58: 23 + 02: 00'', nhưng' chọn ngày (' 2012-09-13T17: 58: 23 + 02: 00 ') 'đã cho 13. –

+0

Điều này không hoạt động nếu trường có' 0 'làm giá trị, như ngày (' 0 ') = 0. Bạn nên thêm ngày (cột) không phải là null và ngày (cột) <> 0. Xem câu trả lời của tôi cho một phương pháp khác –

0

Một chức năng PHP đơn giản mà sẽ xác nhận MySQL kiểu dữ liệu của "ngày" và "datetime" là trong mẫu mã sau (chk_MySQL_Datetime). Nó xác nhận bằng cách sử dụng createFromFormat() để tạo một đối tượng Date cho ngày/ngày nếu điều này không thành công thì ngày/datetime không hợp lệ. Nếu một đối tượng ngày được tạo, một chuỗi được định dạng được tạo với date_format() và được so sánh với ngày/giờ. Nếu các chuỗi bằng ngày/ngày giờ là hợp lệ. Nếu không bằng ngày/ngày giờ không hợp lệ. So sánh này là cần thiết vì createFromFormat() sẽ chấp nhận 2009/02/29 09:85 là một datetime hợp lệ nhưng MySQL sẽ không.

Các mã sau đây bao gồm một trường hợp ví dụ/thử nghiệm cho thấy việc sử dụng và thử nghiệm chống lại một cơ sở dữ liệu MySQL. Để sử dụng ví dụ, bạn sẽ phải thay thế $ database-> domysql_query() bằng cách gọi đến cơ sở dữ liệu. Đầu ra của các echos được bao gồm dưới dạng chú thích ngay lập tức theo các câu lệnh echo.

<?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 
?> 
5

Tôi sử dụng cú pháp này trong Ứng dụng của tôi và nó hoạt động như sự quyến rũ!

SELECT DATE('2013-09-31') AS valid; 

Hệ thống của tôi là Win7x64 với PHP 5.5 và MySQL 5.6.16

+0

Cảm ơn. Hoạt động rất tốt đối với tôi! –

0

Có thể bạn phải tạo cơ sở dữ liệu với công cụ BLACKHOLE, đọc here. Và kiểm tra ngoại lệ được ném bởi 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()); 
    } 
} 
3

Kể từ khi câu hỏi tiểu bang MySQL, đây là một giải pháp MySQL:

Nó là rất khó khăn để xác minh nếu một lĩnh vực là một ngày vì tất cả các định dạng ngày khác nhau có thể sẽ cần Được đưa vào tài khoản. NHƯNG nếu bạn biết rằng các định dạng ngày lĩnh vực là một trong những:

'yyyy-mm-dd' 
'yyyy-mm-dd hh:mm:ss' 
'yyyy-mm-dd whatever' 

Mã này sẽ giúp bạn:

SELECT count(*) FROM `table` 
WHERE DATE(STR_TO_DATE(`column`, '%Y-%m-%d')) IS NOT NULL 
AND `column` NOT REGEXP '^[0-9\.]+$' 

Về cơ bản:

  • điều kiện đầu tiên cho bạn biết nếu là một ngày, nhưng tiếc là không loại trừ các số (ví dụ: DATE(STR_TO_DATE(**1**, '%Y-%m-%d')) = '2001-00-00'
  • giây thứ hai đảm bảo rằng các số được loại trừ, chỉ cho phép bạn có ngày tháng t theo các định dạng trên.

Nếu count(*)>0 thì đó là ngày, nếu đó là 0 thì đó là thứ khác.

Lưu ý Phương pháp này hoạt động cho chuỗi định dạng ngày miễn là bạn biết trước định dạng nào (mà tôi biết không phải lúc nào cũng hữu ích). Chỉ cần thay thế định dạng a priori trong truy vấn

0

DATETIME - trong 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) 

này chỉ nên chọn hợp lệ DATETIME s, cũng nên được áp dụng để DATETIME phù, nhưng không được kiểm tra .. .

Tôi không tìm thấy bất kỳ tài liệu nào trên CAST() giá trị trả về trên chuyển đổi không thành công, nhưng documentation covers a special case - a   có thể thất bại - mà trả NULL:

Đối với chuyển đổi của một chuỗi “zero” sang ngày, CONVERT()CAST() trở NULL và tạo ra một cảnh báo khi chế độ NO_ZERO_DATE SQL là kích hoạt.

Ngoài ra, từ các bài kiểm tra của tôi nó dường như rằng CAST() cư xử giống nhau trên bất kỳ thất bại chuyển đổi - trả NULL và tạo ra cảnh báo.

Tôi thực sự thử nghiệm này trên MariaDB 10.0.30

0

SELECT WEEK(col) IS NOT NULL AS valid;

HOẶC một trong những:

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; 
0

Tôi làm điều này:

if(strtotime(trim($str)) { 
    // persist trim($str) in db.   

} 
Các vấn đề liên quan