milisegundos microsegundos formato fecha ejemplo php mysql doctrine

php - formato - ¿Por qué MySQL no admite una precisión de milisegundos/microsegundos?



microsegundos mysql (8)

Así que encontré el error más frustrante en MySQL .

Aparentemente, el campo TIMESTAMP y las funciones de apoyo no soportan mayor precisión que los segundos.

Así que estoy usando PHP y Doctrine, y realmente necesito esos microsegundos (estoy usando la actAs: [Timestampable] ).

Encontré que puedo usar un campo BIGINT para almacenar los valores. ¿Pero agregará la doctrina los milisegundos? Creo que simplemente asigna NOW () al campo. También me preocupa que las funciones de manipulación de la fecha (en SQL) salpiquen el código.

También vi algo sobre cómo compilar una extensión UDF. Esto no es aceptable porque yo o un futuro mantenedor se actualizará y poof, se habrá cambiado.

¿Alguien ha encontrado una solución adecuada?


¡Encontré una solución! Está muy limpio y no requiere cambios en el código de la aplicación. Esto funciona para Doctrine y también se puede aplicar a otros ORM.

Básicamente, almacene la marca de tiempo como una cadena.

Las comparaciones y la ordenación funcionan si la cadena de fecha tiene el formato correcto. Las funciones de tiempo de MySQL truncarán la porción de microsegundos cuando pasen una cadena de fecha. Esto está bien si la precisión de microsegundos no es necesaria para date_diff etc.

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

Terminé escribiendo una clase MicroTimestampable que implementará esto. Solo actAs:MicroTimestampable mis campos como actAs:MicroTimestampable y voila, precisión microtime con MySQL y 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(); } } } }


Ahora puedes usar micro segundos

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)


Como está utilizando Doctrine para almacenar datos, y Doctrine tampoco admite fracciones de segundo, entonces el cuello de botella no es MySQL.

Sugiero que defina campos adicionales en sus objetos donde necesite la precisión adicional, y almacene en ellos la salida de microtime() . Probablemente desee almacenarlo en dos campos diferentes: uno para la marca de tiempo de epoch seconds y el otro para la parte de microsegundos. De esta forma, puede almacenar enteros estándar de 32 bits y ordenarlos y filtrarlos fácilmente con SQL.

A menudo recomiendo almacenar segundos de época en lugar de los tipos de marca de tiempo nativos ya que generalmente son más fáciles de manipular y evitan el problema de zona horaria completa que sigues teniendo con los tipos de hora nativos y proporcionando servicio a nivel internacional.


Como se mencionó el soporte de microsegundos fue agregado en la versión 5.6.4

Quizás lo siguiente sea útil para fracciones de segundos:

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 ;

Prueba:

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

Otra vista de esto:

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 | +----------------------------+----------------------------+--------+

Consulte la página del Manual de MySQL titulada Segundos fraccionarios en valores de tiempo


Del estándar SQL92:

  • TIMESTAMP - contiene el AÑO, MES, DÍA, HORA, MINUTO y SEGUNDO del campo de fecha y hora.

Una base de datos que cumpla con SQL92 no necesita admitir millones o microsegundos desde mi punto de vista. Por lo tanto, el Bug # 8523 está marcado correctamente como "solicitud de función".

¿Cómo manejará Doctrine los microsegundos y demás? Acabo de encontrar lo siguiente: Doctrine#Timestamp :

El tipo de datos de marca de tiempo es una mera combinación de los tipos de datos de fecha y hora del día. La representación de los valores del tipo de marca de tiempo se logra al unir los valores de cadena de fecha y hora en una sola cadena unida por un espacio. Por lo tanto, la plantilla de formato es AAAA-MM-DD HH: MI: SS.

Por lo tanto, no se mencionan microsegundos como en SQL92-docs. Pero no estoy muy metido en la doctrina, pero parece ser un ORM como Hibernate en Java, por ejemplo. Por lo tanto, podría / debería ser posible definir sus propios modelos, donde puede almacenar la información de tiempo en BIGINT o STRING y su modelo es responsable de leer / escribir en consecuencia en sus clases de PHP.

Por cierto: no espero que MySQL sea compatible con TIMESTAMP con mili / microsegundos en el futuro cercano, por ejemplo, los próximos 5 años.


Desde Mysql versión 5.6.4 en adelante, almacena microsegundos en una columna.

"MySQL ahora admite fracciones de segundo para los valores TIME, DATETIME y TIMESTAMP, con una precisión de hasta microsegundos".

Por ejemplo:

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;

Solo necesita cambiar el tipo de datos a datetime to datetime (6);

Para obtener más información, consulte lo siguiente: dev.mysql.com/doc/refman/5.6/en/fractional-seconds.html


Otra solución para Time en milisegundos. Función creada "time_in_msec"

USO

Diferencia entre dos fechas en milisegundos.

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 ;


Para información para los próximos lectores, este error finalmente se corrigió en la versión 5.6.4 :

"MySQL ahora admite fracciones de segundo para los valores TIME, DATETIME y TIMESTAMP, con una precisión de hasta microsegundos".