SQLite peut utiliser des types de données texte, réels ou entiers pour stocker les dates. De plus, lorsque vous effectuez une requête, les résultats sont affichés au format %Y-%m-%d %H:%M:%S
.
Maintenant, si vous insérez/mettez à jour des valeurs de date/heure en utilisant les fonctions de date/heure de SQLite, vous pouvez également stocker des millisecondes. Si c'est le cas, les résultats sont affichés en utilisant le format %Y-%m-%d %H:%M:%f
. Par exemple :
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
Maintenant, je fais quelques requêtes pour vérifier si nous sommes réellement capables de comparer les temps :
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
Vous pouvez vérifier le même SELECT
en utilisant col2
et col3
et vous obtiendrez les mêmes résultats. Comme vous pouvez le voir, la deuxième ligne (126 millisecondes) n'est pas renvoyée.
Notez que BETWEEN
est inclusif, donc...
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');
... retournera le même ensemble.
Essayez de jouer avec différentes plages de dates/heures et tout se passera comme prévu.
Et sans strftime
fonction ?
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
Et sans strftime
et pas de millisecondes ?
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'en est-il 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
Cela fonctionne très bien.
Enfin, lorsqu'il s'agit d'opérations réelles dans un programme (sans utiliser l'exécutable sqlite...)
BTW : J'utilise JDBC (pas sûr pour les autres langages)... le pilote sqlite-jdbc v3.7.2 à partir de xerial - Peut-être que des révisions plus récentes changent le comportement expliqué ci-dessous... Si vous développez sous Android, vous n'avez pas besoin d'un pilote jdbc. Toutes les opérations SQL peuvent être soumises à l'aide de la fonction SQLiteOpenHelper
.
JDBC dispose de différentes méthodes pour obtenir les valeurs réelles de date et d'heure d'une base de données : java.sql.Date
, java.sql.Time
et java.sql.Timestamp
.
Les méthodes connexes dans java.sql.ResultSet
sont (évidemment) getDate(..)
, getTime(..)
et getTimestamp()
respectivement.
Par exemple :
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.
Puisque SQLite n'a pas de type de données DATE/TIME/TIMESTAMP, ces 3 méthodes retournent des valeurs comme si les objets étaient initialisés avec 0 :
new java.sql.Date(0)
new java.sql.Time(0)
new java.sql.Timestamp(0)
La question qui se pose est donc la suivante : comment sélectionner, insérer ou mettre à jour des objets de type date/heure/horodatage ? Il n'y a pas de réponse facile. Vous pouvez essayer différentes combinaisons, mais elles vous obligeront à intégrer des fonctions SQLite dans toutes les instructions SQL. Il est beaucoup plus facile de définir une classe utilitaire pour transformer le texte en objets Date dans votre programme Java. Mais n'oubliez jamais que SQLite transforme toute valeur de date en UTC+0000.
En résumé, malgré la règle générale consistant à toujours utiliser le bon type de données, ou même des entiers indiquant le temps Unix (millisecondes depuis l'époque), je trouve beaucoup plus facile d'utiliser le format SQLite par défaut ( '%Y-%m-%d %H:%M:%f'
ou en Java 'yyyy-MM-dd HH:mm:ss.SSS'
) plutôt que de compliquer toutes vos déclarations SQL avec des fonctions SQLite. La première approche est beaucoup plus facile à maintenir.
TODO : Je vais vérifier les résultats en utilisant getDate/getTime/getTimestamp dans Android (API15 ou mieux)... peut-être que le pilote interne est différent de sqlite-jdbc...