Почему MySQL не поддерживает миллисекунду / точность микросекунды?

Таким образом, я просто находил самую расстраивающую ошибку когда-либо в MySQL.

По-видимому, TIMESTAMP поле и поддерживающие функции не поддерживают большей точности, чем секунды!?

Таким образом, я использую PHP и Доктрину, и мне действительно нужно в те микросекунды (я использую actAs: [Timestampable] свойство).

Я нашел, что я могу использовать a BIGINT поле для хранения значений. Но доктрина добавит миллисекунды? Я думаю, что это просто присваивается ТЕПЕРЬ () к полю. Я также волнуюсь, что функции управления датой (в SQL) опрыснутый через код повредятся.

Я также видел что-то о компиляции расширения UDF. Это не приемлемое, потому что я или будущий специалист по обслуживанию обновим и пуф, изменение, которое уводят.

Кто-либо нашел подходящее обходное решение?

41
задан Byron Whitlock 3 April 2010 в 18:45
поделиться

4 ответа

Я нашел обходной путь! Он очень чистый и не требует изменения кода приложения. Это работает для 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();
            }
        }
    }
}
9
ответ дан 27 November 2019 в 00:29
поделиться

Из стандарта SQL92:

  • TIMESTAMP - содержит ГОД поля datetime, MONTH, DAY, HOUR, MINUTE и SECOND.

С моей точки зрения, база данных, совместимая с SQL92, не должна поддерживать миллисекунды или микросекунды. Поэтому ошибка № 8523 правильно помечена как «запрос функции».

Как Doctrine будет обрабатывать микросекунды и др.? Я только что нашел следующее: Doctrine # Timestamp :

Тип данных timestamp представляет собой простую комбинацию даты и времени дневные типы данных. представление значений типа штампа времени достигается путем объединения значений строки даты и времени в единую строку , соединенную пробелом. {{ 1}} Следовательно, шаблон формата - ГГГГ-ММ-ДД ЧЧ: МИ: СС.

Таким образом, микросекунды не упоминаются, как в документации по SQL92. Но я не слишком углубляюсь в доктрину, но, похоже, это ORM, например, спящий режим в java. Следовательно, можно / должно быть возможно определить ваши собственные модели, где вы можете хранить информацию о времени в BIGINT или STRING, и ваша модель отвечает за чтение / запись ее соответственно в ваши PHP-классы.

Кстати: я не ожидаю, что MySQL будет поддерживать TIMESTAMP с милли / микросекундами в ближайшем будущем, например, в ближайшие 5 лет.

29
ответ дан 27 November 2019 в 00:29
поделиться

Поскольку вы используете Doctrine для хранения данных, а Doctrine также не поддерживает дробные секунды, то узким местом является не MySQL.

Я предлагаю вам определить дополнительные поля в ваших объектах, где вам нужна дополнительная точность, и сохранить в них вывод microtime () . Вероятно, вы захотите сохранить его в двух разных полях - одно для метки времени в секундах эпохи, а другое для части микросекунд. Таким образом, вы можете хранить стандартные 32-битные целые числа и легко сортировать и фильтровать их с помощью SQL.

Я часто рекомендую хранить секунды эпохи вместо собственных типов временных меток, так как ими обычно легче манипулировать и избежать проблем с часовыми поясами, с которыми вы все время сталкиваетесь с собственными типами времени и предоставлением услуг на международном уровне.

4
ответ дан 27 November 2019 в 00:29
поделиться

Другой способ решения проблемы времени в миллисекундах. Создана функция "time_in_msec"

ИСПОЛЬЗОВАНИЕ:

Разница между двумя датами в миллисекундах.

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 ;
3
ответ дан 27 November 2019 в 00:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: