La version 2008 de SQL Server introduit de nouveaux types de données liés aux dates et aux heures. Après une brève présentation de ces nouveautés, je mentionnerai quelques cas dans lesquels leur utilisation à la place de l’ancien type datetime nécessitera quelques précautions…
Tout d’abord, une brève présentation. En complément du type datetime présent dans SQL Server dans les versions précédentes et qui est toujours disponible, la version 2008 apporte 4 nouveaux types : time, date, datetime2 et datetimeoffset.
Voici un bref résumé des caractéristiques de chacun de ces types :
Type | Plage | Précision | Taille de stockage |
datetime | 01/01/1753 – 31/12/9999 | 10/3 millisecondes | 8 octets |
date | 01/01/0001 – 31/12/999 | 1 jour | 3 octets |
time(n) | 00:00:00:00.0000000 – 23:59:59:9999999 | 10-n secondes | 0<=n<=2 : 3 octets 3<=n<=4 : 4 octets 5<=n<=7 : 5 octets |
datetime2(n) | Plage du type date + plage du time(n) | 10-n secondes | 0<=n<=2 : 6 octets 3<=n<=4 : 7 octets 5<=n<=7 : 8 octets |
datetimeoffset(n) | Plage du type date + plage du time(n) Plage de décalage de fuseau horaire : 14:00 à 14:00 |
10-n secondes | 0<=n<=2 : 8 octets 3<=n<=4 : 9 octets 5<=n<=7 : 10 octets |
Je ne vais pas chercher à réécrire ici l’aide en ligne, d’autres l’ont fait mieux que moi… : http://msdn.microsoft.com/fr-fr/library/ms186724.aspx
Voici quelques exemples dans lesquels ces nouveaux types de données peuvent être intéressants :
– Stockage de dates (de naissance, d’événements particuliers, …) sans s’embarrasser d’octets superflus pour stocker une heure inutile. En remplaçant le type datetime par le type date, on économise plus de 60% d’espace disque ! De même, lorsque l’on cherche à modifier des heures (par exemple dans la gestion de l’emploi d’un emploi du temps scolaire), on peut là encore économiser plus de 60% d’espace disque…
– Stockage de dates d’événements historiques : aussi curieux que cela paraisse, et sans évoquer les types de données utilisateurs réalisables en .NET sous SQL Server 2005 ou l’utilisation d’un pseudo offset, il n’était pas possible avant SQL Server 2008 de réaliser une table d’événements historiques (avec une colonne de date) couvrant des événements majeurs tels que la célèbre bataille de Marignan (1515 pour ceux et celles pour qui les cours d’histoire sont un peu trop lointains…). Désormais, le nouveau type de données date (et les types plus complets datetime2 et datetimeoffset) permettent nativement de mémoriser de telles dates.
Par contre, voici quelques points de vigilance pour ceux qui souhaitent faire basculer certaines des colonnes de leurs tables du type datetime aux types date ou datetime2.
Construisons par exemple une fonction f_Lendemain prenant en entrée un paramètre de type datetime et renvoyant le lendemain de cette date.
USE [tempdb] GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[f_Lendemain]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION [dbo].[f_Lendemain] GO create function [dbo].[f_Lendemain] (@Jour datetime) returns datetime begin return (@Jour+1) end GO
Je sais, ce n’est pas super propre comme façon de coder, mais bon, ça marche…
select dbo.f_Lendemain('20100101') as Reponse
Reponse
———————–
2010-01-02 00:00:00.000
Par contre, la limite de date se fait bien sûr ressentir…
select dbo.f_Lendemain('15150101') as Reponse
Reponse
———————–
Msg 242, Level 16, State 3, Line 1
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
Désormais, nous avons à notre disposition de nouveaux types de données, alors profitons-en !
USE [tempdb] GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[f_Lendemain2]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION [dbo].[f_Lendemain2] GO create function [dbo].[f_Lendemain2] (@Jour date) returns date begin return (@Jour+1) end GO
Msg 206, Level 16, State 2, Procedure f_Lendemain, Line 4
Operand type clash: date is incompatible with int
Tiens, c’est nouveau ça ? Et bien oui, désormais, fini le code pas propre, une date n’est pas à considérer comme un nombre réel comme c’était implicitement le cas avant (nombre de jours avant la virgule, et fraction de journée pour la suite). Une date, c’est une date, sans que l’on ait à s’occuper de la manière dont le système la stocke en interne. Et pour ajouter une journée, codons proprement, avec les fonctions dédiées !
USE [tempdb] GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[f_Lendemain2]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION [dbo].[f_Lendemain2] GO create function [dbo].[f_Lendemain2] (@Jour date) returns date begin return DATEADD(day,1,@Jour) end GO
Là, enfin, ça compile. Et surtout, ça fonctionne.
select dbo.f_Lendemain2('15150101') as Reponse
Reponse
———-
1515-01-02
Mais en grattant un peu plus, on s’apperçoit que tout n’est pas si rose, et que quelques cas particuliers montrent des différences de comportement…
set dateformat ydm select dbo.f_Lendemain('2010-02-03') as [Version datetime], dbo.f_Lendemain2('2010-02-03') as [Version datetime2]
Version datetime Version datetime2
———————– —————–
2010-03-03 00:00:00.000 2010-02-04
Ce format de date est assez peut usité, mais il faut effectivement ne pas oublier de lire complètement l’aide en ligne pour ne pas avoir de mauvaises surprises, et il est clairement indiqué ici que Le paramètre DATEFORMAT ydm n’est pas pris en charge pour les types de données date, datetime2 et datetimeoffset.
Pour plus de détails, l’aide en ligne présente notamment les formats conseillés pour la représentation sous forme de chaîne de caractères des dates (http://msdn.microsoft.com/fr-fr/library/ms180878.aspx#StringLiteralDateandTimeFormats).
Voici donc pour une première approche montrant notamment qu’il convient de ne pas chercher à se jeter de suite sur les nouveautés sans précautions et aussi qu’il vaut toujours mieux éviter les petits passe-droits de codage tolérés à un instant t…