代码之家  ›  专栏  ›  技术社区  ›  Byron Whitlock

为什么MySQL不支持毫秒/微秒精度?

  •  41
  • Byron Whitlock  · 技术社区  · 15 年前

    所以我发现 the most frustrating bug ever in MySQL .

    TIMESTAMP 现场,以及 supporting functions

    所以我使用的是PHP和条令,我真的需要这些微秒(我使用的是 actAs: [Timestampable] 属性)。

    我找到了一个我可以用的 BIGINT 字段来存储值。但是条令会增加毫秒吗?我想它只是把NOW()赋值给这个字段。我还担心代码中的日期操作函数(SQL)会中断。

    我还看到了一些关于编译UDF扩展的内容。这是不可接受的,因为我或未来的维护人员将升级和poof,改变消失。

    有人找到合适的解决方法吗?

    8 回复  |  直到 15 年前
        1
  •  36
  •   Xavier Portebois    13 年前

    为了给下一个读者提供信息,这个错误最终在版本中被纠正了 :

    MySQL现在支持时间、日期时间和时间戳值的小数秒,精度高达微秒

        2
  •  29
  •   Michael Konietzka    13 年前

    根据SQL92标准:

    • TIMESTAMP-包含datetime字段的年、月、日, 时、分、秒。

    从我的观点来看,兼容SQL92的数据库不需要支持毫秒或微秒。因此,Bug#8523被正确地标记为“featurerequest”。

    条令如何处理微秒等?我刚刚发现了以下几点: Doctrine#Timestamp :

    日期和时间的组合 一天中的数据类型。这个 时间价值的表示 图章类型是通过连接完成的 数据中的日期和时间字符串值 因此,格式模板是 YYYY-MM-DD时:分:秒。

        3
  •  9
  •   Byron Whitlock    15 年前

    基本上,将时间戳存储为字符串。

    date_diff 等。

    SELECT DATEDIFF('2010-04-04 17:24:42.000000','2010-04-04 17:24:42.999999');
    > 0
    
    SELECT microsecond('2010-04-04 17:24:42.021343');
    > 21343 
    

    最后我写了一封信 MicroTimestampable 类来实现此。我只是把我的字段注释为 actAs:MicroTimestampable

    class Doctrine_Template_MicroTimestampable extends Doctrine_Template_Timestampable
    {
        /**
         * Array of Timestampable options
         *
         * @var string
         */
        protected $_options = array('created' =>  array('name'          =>  'created_at',
                                                        'alias'         =>  null,
                                                        'type'          =>  'string(30)',
                                                        'format'        =>  'Y-m-d H:i:s',
                                                        'disabled'      =>  false,
                                                        'expression'    =>  false,
                                                        'options'       =>  array('notnull' => true)),
                                    'updated' =>  array('name'          =>  'updated_at',
                                                        'alias'         =>  null,
                                                        'type'          =>  'string(30)',
                                                        'format'        =>  'Y-m-d H:i:s',
                                                        'disabled'      =>  false,
                                                        'expression'    =>  false,
                                                        'onInsert'      =>  true,
                                                        'options'       =>  array('notnull' => true)));
    
        /**
         * Set table definition for Timestampable behavior
         *
         * @return void
         */
        public function setTableDefinition()
        {
            if ( ! $this->_options['created']['disabled']) {
                $name = $this->_options['created']['name'];
                if ($this->_options['created']['alias']) {
                    $name .= ' as ' . $this->_options['created']['alias'];
                }
                $this->hasColumn($name, $this->_options['created']['type'], null, $this->_options['created']['options']);
            }
    
            if ( ! $this->_options['updated']['disabled']) {
                $name = $this->_options['updated']['name'];
                if ($this->_options['updated']['alias']) {
                    $name .= ' as ' . $this->_options['updated']['alias'];
                }
                $this->hasColumn($name, $this->_options['updated']['type'], null, $this->_options['updated']['options']);
            }
    
            $this->addListener(new Doctrine_Template_Listener_MicroTimestampable($this->_options));
        }
    }
    

    条令\u模板\u侦听器\u微时间表

    class Doctrine_Template_Listener_MicroTimestampable extends Doctrine_Template_Listener_Timestampable
    {
        protected $_options = array();
    
        /**
         * __construct
         *
         * @param string $options 
         * @return void
         */
        public function __construct(array $options)
        {
            $this->_options = $options;
        }
    
        /**
         * Gets the timestamp in the correct format based on the way the behavior is configured
         *
         * @param string $type 
         * @return void
         */
        public function getTimestamp($type, $conn = null)
        {
            $options = $this->_options[$type];
    
            if ($options['expression'] !== false && is_string($options['expression'])) {
                return new Doctrine_Expression($options['expression'], $conn);
            } else {
                if ($options['type'] == 'date') {
                    return date($options['format'], time().".".microtime());
                } else if ($options['type'] == 'timestamp') {
                    return date($options['format'], time().".".microtime());
                } else {
                    return time().".".microtime();
                }
            }
        }
    }
    
        4
  •  4
  •   Guss    15 年前

    当您使用条令存储数据时,条令也不支持分秒,那么瓶颈就不是MySQL了。

    microtime() . 您可能希望将其存储在两个不同的字段中—一个用于epoch秒时间戳,另一个用于微秒部分。这样,您就可以存储标准的32位整数,并使用SQL轻松地对它们进行排序和筛选。

        5
  •  3
  •   Daniel Vassallo    15 年前

    另一个以毫秒为单位的解决方法。已创建函数“时间(毫秒)”

    用法:

    mysql> SELECT time_in_msec('2010-07-12 23:14:36.233','2010-07-11 23:04:00.000') AS miliseconds;
    +-------------+
    | miliseconds |
    +-------------+
    | 87036233    |
    +-------------+
    1 row in set, 2 warnings (0.00 sec)
    
    
    
    DELIMITER $$
    
    DROP FUNCTION IF EXISTS `time_in_msec`$$
    
    CREATE FUNCTION `time_in_msec`(ftime VARCHAR(23),stime VARCHAR(23)) RETURNS VARCHAR(30) CHARSET latin1
    BEGIN
        DECLARE msec INT DEFAULT 0;
        DECLARE sftime,sstime VARCHAR(27);
        SET ftime=CONCAT(ftime,'000');
        SET stime=CONCAT(stime,'000');
        SET  msec=TIME_TO_SEC(TIMEDIFF(ftime,stime))*1000+TRUNCATE(MICROSECOND(TIMEDIFF(ftime,stime))/1000,0);
        RETURN msec;
    END$$
    
    DELIMITER ;
    
        6
  •  3
  •   Voitcus    12 年前

    从MySQL5.6.4开始,它在一列中存储微秒。

    例如:

    CREATE TABLE `test_table` (
    `name` VARCHAR(1000) ,
    `orderdate` DATETIME(6)
    );
    
    INSERT INTO test_table VALUES('A','2010-12-10 14:12:09.019473');
    
    SELECT * FROM test_table;
    

    只需将数据类型更改为datetime到datetime(6);

    有关更多信息,请参阅以下内容: http://dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html

        7
  •  2
  •   m00am    9 年前

    mysql> select @@version;
    +-----------+
    | @@version |
    +-----------+
    | 5.6.26    |
    +-----------+
    1 row in set (0.00 sec)
    
    mysql> select now(6);
    +----------------------------+
    | now(6)                     |
    +----------------------------+
    | 2016-01-16 21:18:35.496021 |
    +----------------------------+
    1 row in set (0.00 sec)
    
        8
  •  1
  •   Drew    8 年前

    如前所述,5.6.4版中添加了微秒支持

    也许以下是分数秒的用法:

    drop procedure if exists doSomething123;
    delimiter $$
    create procedure doSomething123()
    begin
        DECLARE dtBEGIN,dtEnd DATETIME(6);
        DECLARE theCount,slp INT;
        set dtBegin=now(6); -- see http://dev.mysql.com/doc/refman/5.7/en/fractional-seconds.html
    
        -- now do something to profile
        select count(*) into theCount from questions_java where closeDate is null;
        select sleep(2) into slp;
        -- not the above but "something"
    
        set dtEnd=now(6); -- see http://dev.mysql.com/doc/refman/5.7/en/fractional-seconds.html
        select timediff(dtEnd,dtBegin) as timeDiff,timediff(dtEnd,dtBegin)+MICROSECOND(timediff(dtEnd,dtBegin))/1000000 seconds;
        -- select dtEnd,dtBegin;
    end$$
    delimiter ;
    

    测试:

    call doSomething123();
    +-----------------+----------+
    | timeDiff        | seconds  |
    +-----------------+----------+
    | 00:00:02.008378 | 2.016756 |
    +-----------------+----------+
    

    另一种观点:

    set @dt1=cast('2016-01-01 01:00:00.1111' as datetime(6)); 
    set @dt2=cast('2016-01-01 01:00:00.8888' as datetime(6)); 
    
    select @dt1,@dt2,MICROSECOND(timediff(@dt2,@dt1))/1000000 micros;
    +----------------------------+----------------------------+--------+
    | @dt1                       | @dt2                       | micros |
    +----------------------------+----------------------------+--------+
    | 2016-01-01 01:00:00.111100 | 2016-01-01 01:00:00.888800 | 0.7777 |
    +----------------------------+----------------------------+--------+
    

    请参阅MySQL手册中题为 Fractional Seconds in Time Values

    推荐文章