SQLite puede usar tipos de datos de texto, reales o enteros para almacenar fechas. Aún más, cada vez que realiza una consulta, los resultados se muestran usando el formato%Y-%m-%d %H:%M:%S
.
Ahora, si inserta / actualiza valores de fecha / hora utilizando las funciones de fecha / hora de SQLite, en realidad también puede almacenar milisegundos. Si ese es el caso, los resultados se muestran usando el formato %Y-%m-%d %H:%M:%f
. Por ejemplo:
sqlite> create table test_table(col1 text, col2 real, col3 integer);
sqlite> insert into test_table values (
strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123'),
strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123'),
strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123')
);
sqlite> insert into test_table values (
strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.126'),
strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.126'),
strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.126')
);
sqlite> select * from test_table;
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126
Ahora, haciendo algunas consultas para verificar si realmente podemos comparar los tiempos:
sqlite> select * from test_table /* using col1 */
where col1 between
strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.121') and
strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.125');
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
Puede comprobar el mismo SELECT
usando col2
y col3
y obtendrá los mismos resultados. Como puede ver, la segunda fila (126 milisegundos) no se devuelve.
Tenga en cuenta que BETWEEN
es inclusivo, por lo tanto ...
sqlite> select * from test_table
where col1 between
/* Note that we are using 123 milliseconds down _here_ */
strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.123') and
strftime('%Y-%m-%d %H:%M:%f', '2014-03-01 13:01:01.125');
... devolverá el mismo conjunto.
Intenta jugar con diferentes rangos de fecha / hora y todo se comportará como se espera.
¿Qué pasa sin strftime
función?
sqlite> select * from test_table /* using col1 */
where col1 between
'2014-03-01 13:01:01.121' and
'2014-03-01 13:01:01.125';
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
¿Qué pasa sin strftime
función y sin milisegundos?
sqlite> select * from test_table /* using col1 */
where col1 between
'2014-03-01 13:01:01' and
'2014-03-01 13:01:02';
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126
¿Qué hay de ORDER BY
?
sqlite> select * from test_table order by 1 desc;
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
sqlite> select * from test_table order by 1 asc;
2014-03-01 13:01:01.123|2014-03-01 13:01:01.123|2014-03-01 13:01:01.123
2014-03-01 13:01:01.126|2014-03-01 13:01:01.126|2014-03-01 13:01:01.126
Funciona bien
Finalmente, cuando se trata de operaciones reales dentro de un programa (sin usar el ejecutable sqlite ...)
Por cierto: estoy usando JDBC (no estoy seguro acerca de otros idiomas) ... el controlador sqlite-jdbc v3.7.2 de xerial - tal vez las revisiones más recientes cambien el comportamiento explicado a continuación ... Si está desarrollando en Android, no Necesito un controlador jdbc. Todas las operaciones SQL se pueden enviar utilizando elSQLiteOpenHelper
.
JDBC tiene diferentes métodos para obtener valores reales de fecha / hora a partir de una base de datos: java.sql.Date
, java.sql.Time
, y java.sql.Timestamp
.
Los métodos relacionados en java.sql.ResultSet
son (obviamente) getDate(..)
, getTime(..)
y getTimestamp()
respectivamente.
Por ejemplo:
Statement stmt = ... // Get statement from connection
ResultSet rs = stmt.executeQuery("SELECT * FROM TEST_TABLE");
while (rs.next()) {
System.out.println("COL1 : "+rs.getDate("COL1"));
System.out.println("COL1 : "+rs.getTime("COL1"));
System.out.println("COL1 : "+rs.getTimestamp("COL1"));
System.out.println("COL2 : "+rs.getDate("COL2"));
System.out.println("COL2 : "+rs.getTime("COL2"));
System.out.println("COL2 : "+rs.getTimestamp("COL2"));
System.out.println("COL3 : "+rs.getDate("COL3"));
System.out.println("COL3 : "+rs.getTime("COL3"));
System.out.println("COL3 : "+rs.getTimestamp("COL3"));
}
// close rs and stmt.
Como SQLite no tiene un tipo de datos DATE / TIME / TIMESTAMP real, estos 3 métodos devuelven valores como si los objetos se inicializaran con 0:
new java.sql.Date(0)
new java.sql.Time(0)
new java.sql.Timestamp(0)
Entonces, la pregunta es: ¿cómo podemos seleccionar, insertar o actualizar objetos de fecha / hora / marca de tiempo?No hay una respuesta fácil. Puede probar diferentes combinaciones, pero lo obligarán a incrustar funciones SQLite en todas las instrucciones SQL. Es mucho más fácil definir una clase de utilidad para transformar texto en objetos Date dentro de su programa Java. Pero recuerde siempre que SQLite transforma cualquier valor de fecha a UTC + 0000.
En resumen, a pesar de la regla general de usar siempre el tipo de datos correcto o, incluso, enteros que denotan el tiempo de Unix (milisegundos desde la época), me resulta mucho más fácil usar el formato predeterminado de SQLite ( '%Y-%m-%d %H:%M:%f'
o en Java 'yyyy-MM-dd HH:mm:ss.SSS'
) en lugar de complicar todas sus declaraciones SQL con Funciones SQLite. El primer enfoque es mucho más fácil de mantener.
TODO: Comprobaré los resultados cuando use getDate / getTime / getTimestamp dentro de Android (API15 o superior) ... tal vez el controlador interno sea diferente de sqlite-jdbc ...