一尘不染

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

mysql

因此,我刚刚发现MySQL中最令人沮丧的错误

显然,该TIMESTAMP字段及其支持功能不支持比秒更高的精度!

所以我正在使用PHP和Doctrine,我真的需要那些微秒(我正在使用actAs: [Timestampable]属性)。

我发现我可以使用一个BIGINT字段来存储值。但是学说会增加毫秒吗?我认为它只是将NOW()分配给该字段。我还担心通过代码散布的日期操作功能(在SQL中)会中断。

我还看到了有关编译UDF扩展的内容。这是不可接受的,因为我或将来的维护者会升级,烦恼,更改不见了。

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


阅读 305

收藏
2020-05-17

共1个答案

一尘不染

我找到了解决方法!它非常干净,不需要更改任何应用程序代码。这适用于Doctrine,也可以应用于其他ORM。

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

如果日期字符串格式正确,则比较和排序有效。当传递日期字符串时,MySQL时间函数将截断微秒部分。如果不需要微秒精度,则可以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用MySQL和Doctrine来注释我的字段,等等,时间精度。

Doctrine_Template_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));
    }
}

Doctrine_Template_Listener_MicroTimestampable

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();
            }
        }
    }
}
2020-05-17