Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

AT TIME ZONE - une nouvelle fonctionnalité préférée dans SQL Server 2016


Image © Mark Boyle | Australia Day Council of NSW.
Images propriété des artistes respectifs. Tous droits réservés.

AT TIME ZONE est une fonctionnalité tellement intéressante que je ne l'avais pas remarquée jusqu'à récemment, même si Microsoft a publié une page à ce sujet depuis décembre.

Je vis à Adélaïde en Australie. Et comme plus d'un milliard d'autres personnes dans le monde, les habitants d'Adélaïde doivent faire face à un fuseau horaire d'une demi-heure. En hiver, nous sommes UTC+9:30, et en été, nous sommes UTC+10:30. Sauf que si vous lisez ceci dans l'hémisphère nord, vous devrez vous rappeler que par "hiver", je veux dire d'avril à octobre. L'heure d'été est d'octobre à avril, et le Père Noël est assis sur la plage avec une boisson fraîche, transpirant à travers son épais costume rouge et sa barbe. À moins qu'il ne sauve des vies, bien sûr.

En Australie, nous avons trois fuseaux horaires principaux (ouest, centre et est), mais cela s'étend à cinq en été, car les trois États qui s'étendent jusqu'à l'extrémité nord de l'Australie (WA, Qld et NT) ne le font pas. essayez de sauver n'importe quelle lumière du jour. Ils sont assez près de l'équateur pour ne pas s'en soucier, ou quelque chose comme ça. C'est très amusant pour l'aéroport de Gold Coast, dont la piste traverse la frontière NSW-QLD.

Les serveurs de base de données s'exécutent souvent en UTC, car il est tout simplement plus facile de ne pas avoir à faire la conversion entre UTC et local (et inversement) dans SQL Server. Il y a de nombreuses années, je me souviens avoir dû corriger un rapport qui énumérait les incidents survenus ainsi que les temps de réponse (j'ai blogué à ce sujet depuis). Mesurer le SLA était assez simple - j'ai pu voir qu'un incident s'est produit pendant les heures de travail du client et qu'il a répondu dans l'heure. J'ai pu voir qu'un autre incident s'est produit en dehors des heures de travail et la réponse a été dans les deux heures. Le problème est survenu lorsqu'un rapport a été produit à la fin d'une période où le fuseau horaire a changé, ce qui a fait qu'un incident qui s'est réellement produit à 17h30 (en dehors des heures) a été répertorié comme s'il s'était produit à 16h30 (en dehors des heures). . La réponse avait pris environ 90 minutes, ce qui était correct, mais le rapport montrait le contraire.

Tout cela est corrigé dans SQL Server 2016.

Comment utiliser AT TIME ZONE dans SQL Server 2016

Maintenant, avec AT TIME ZONE, au lieu de dire :'20160101 00:00 +10:30', je peux commencer avec une valeur datetime qui n'a pas de décalage de fuseau horaire, et utiliser AT TIME ZONE pour expliquer que c'est à Adélaïde.

SELECT CONVERT(datetime,'20160101 00:00') AT TIME ZONE 'Cen. Heure normale de l'Australie » ; -- 2016-01-01 00:00:00.000 +10:30

Et cela peut être converti à l'heure américaine en ajoutant à nouveau AT TIME ZONE.

SELECT CONVERT(datetime,'20160101 00:00') AT TIME ZONE 'Cen. Heure normale d'Australie' AT TIME ZONE 'US Eastern Standard Time' ; -- 2015-12-31 08:30:00.000 -05:00

Maintenant, je sais que c'est beaucoup plus long. Et j'ai besoin de convertir explicitement la chaîne en datetime, pour éviter une erreur disant :

Le type de données d'argument varchar n'est pas valide pour l'argument 1 de la fonction AT TIME ZONE.

Mais malgré sa longueur, j'adore ça, car à aucun moment je n'ai eu besoin de comprendre qu'Adélaïde était à +10h30, ou que Eastern était à -5h00 - j'avais simplement besoin de connaître le fuseau horaire par son nom. Déterminer si l'heure d'été doit s'appliquer ou non a été géré pour moi, et je n'ai pas eu à faire de conversion de local à UTC pour établir une base de référence.

Cela fonctionne en utilisant le registre Windows, qui contient toutes ces informations, mais malheureusement, ce n'est pas parfait quand on regarde en arrière dans le temps. L'Australie a changé les dates en 2008 et les États-Unis ont changé les siennes en 2005 - les deux pays conservant la lumière du jour pendant une plus grande partie de l'année. AT TIME ZONE l'a bien compris. Mais il ne semble pas apprécier qu'en Australie en l'an 2000, grâce aux Jeux olympiques de Sydney, l'Australie a commencé l'heure d'été environ deux mois plus tôt. C'est un peu frustrant, mais ce n'est pas la faute de SQL - nous devons blâmer Windows pour cela. Je suppose que le registre Windows ne se souvient pas du correctif qui s'est produit cette année-là. (Note personnelle :je devrai peut-être demander à quelqu'un de l'équipe Windows de résoudre ce problème...)

L'utilité continue cependant !

Ce nom de fuseau horaire n'a même pas besoin d'être une constante. Je peux transmettre des variables et même utiliser des colonnes :

AVEC PeopleAndTZs AS( SELECT * FROM (VALUES ('Rob', 'Cen. Australia Standard Time'), ('Paul', 'New Zealand Standard Time'), ('Aaron', 'US Eastern Standard Time' ) ) t (personne, tz))SELECT tz.person, SYSDATETIMEOFFSET() AT TIME ZONE tz.tz FROM PeopleAndTZs tz ; /* Rob 2016-07-18 18:29:11.9749952 +09:30 Paul 2016-07-18 20:59:11.9749952 +12:00 Aaron 2016-07-18 04:59:11.9749952 -04:00*/ 

(Parce que j'ai couru ça juste avant 18h30 ici à Adélaïde, qui se trouve être près de 21h en Nouvelle-Zélande où se trouve Paul, et près de 5h ce matin dans l'est de l'Amérique où se trouve Aaron.)

Cela me permettrait de voir facilement l'heure qu'il est pour les gens où qu'ils se trouvent dans le monde, et de voir qui serait le mieux placé pour répondre à un problème, sans avoir à effectuer de conversion date/heure manuelle. Et plus encore, cela me permettrait de le faire pour les gens du passé. Je pourrais avoir un rapport qui analyse les fuseaux horaires permettant le plus grand nombre d'événements pendant les heures de bureau.

Ces fuseaux horaires sont répertoriés dans sys.time_zone_info , ainsi que le décalage actuel et si l'heure d'été est actuellement appliquée.

nom

current_utc_offset

is_currently_dst
Heure normale de Singapour

+08:00

0
W. Heure standard de l'Australie

+08:00

0
Heure normale de Taipei

+08:00

0
Heure normale d'Oulan-Bator

+09:00

1
Heure standard de la Corée du Nord

+08:30

0
Heure normale d'Aus Central W.

+08:45

0
Heure normale de Transbaikal

+09:00

0
Heure normale de Tokyo

+09:00

0

Échantillonnage des lignes de sys.time_zone_info

Je ne suis vraiment intéressé que par le nom, mais de toute façon. Et il est intéressant de voir qu'il existe un fuseau horaire appelé "Aus Central W. Standard Time" qui est au quart d'heure. Allez comprendre. Il convient également de noter que les lieux sont désignés par leur nom d'heure standard, même s'ils observent actuellement l'heure d'été. Comme Ulaanbaatar dans cette liste ci-dessus, qui n'est pas répertoriée comme Ulaanbaatar Daylight Time. Cela peut jeter les gens dans une boucle lorsqu'ils commencent à utiliser AT TIME ZONE.

AT TIME ZONE peut-il causer des problèmes de performances ?

Maintenant, je suis sûr que vous vous demandez quel pourrait être l'impact de l'utilisation d'AT TIME ZONE sur l'indexation.

En termes de forme du plan, ce n'est pas différent de traiter avec datetimeoffset en général. Si j'ai des valeurs datetime, comme dans la colonne Sales.SalesOrderHeader.OrderDate d'AdventureWorks (sur laquelle j'ai créé un index appelé rf_IXOD), j'exécute alors cette requête :

select OrderDate, SalesOrderID from Sales.SalesOrderHeader where OrderDate>=convert(datetime,'20110601 00:00') at time zone 'US Eastern Standard Time' and OrderDate  

Et cette requête :

select OrderDate, SalesOrderID from Sales.SalesOrderHeader where OrderDate>=convert(datetimeoffset,'20110601 00:00 -04:00') and OrderDate  

Dans les deux cas, vous obtenez des plans qui ressemblent à ceci :

Mais si on explore d'un peu plus près, il y a un problème.

Celui qui utilise AT TIME ZONE n'utilise pas très bien les statistiques. Il pense qu'il va voir 5 170 lignes sortir de cet Index Seek, alors qu'il n'y en a en fait que 217. Pourquoi 5 170 ? Eh bien, le récent article d'Aaron, "Attention aux estimations", l'explique, en se référant à l'article "Estimation de cardinalité pour plusieurs prédicats" de Paul. 5 170 correspond à 31 465 (lignes du tableau) * 0,3 * sqrt(0,3).

La deuxième requête est correcte, en estimant 217. Aucune fonction n'est impliquée, vous voyez.

C'est probablement assez juste. Je veux dire - au moment où il produit le plan, il n'aura pas demandé au registre les informations dont il a besoin, donc il ne sait vraiment pas combien estimer. Mais il est possible que ce soit un problème.

Si j'ajoute des prédicats supplémentaires dont je sais qu'ils ne peuvent pas poser de problème, mes estimations chutent encore plus - jusqu'à 89,9 lignes.

select OrderDate, SalesOrderID from Sales.SalesOrderHeader where OrderDate>=convert(datetime,'20110601 00:00') at time zone 'US Eastern Standard Time' and OrderDate =convert(datetimeoffset,'20110601 00:00 +14:00') et OrderDate  

Estimer trop de lignes signifie que trop de mémoire est allouée, mais estimer trop peu peut entraîner trop peu de mémoire, avec potentiellement besoin d'un débordement pour corriger le problème (ce qui peut souvent être désastreux du point de vue des performances). Allez lire le message d'Aaron pour plus d'informations sur la façon dont les mauvaises estimations peuvent être mauvaises.

Lorsque je considère comment gérer l'affichage des valeurs pour ces personnes d'avant, je peux utiliser des requêtes comme celle-ci :

AVEC PeopleAndTZs AS( SELECT * FROM (VALUES ('Rob', 'Cen. Australia Standard Time'), ('Paul', 'New Zealand Standard Time'), ('Aaron', 'US Eastern Standard Time' ) ) t (personne, tz))SELECT tz.person, o.SalesOrderID, o.OrderDate AT TIME ZONE 'UTC' AT TIME ZONE tz.tzFROM PeopleAndTZs tzCROSS JOIN Sales.SalesOrderHeader oWHERE o.SalesOrderID BETWEEN 44001 AND 44010; 

Et obtenez ce plan :

… qui n'a pas de telles préoccupations - le scalaire de calcul le plus à droite convertit le datetime OrderDate en datetimeoffset pour UTC, et le scalaire de calcul le plus à gauche le convertit dans le fuseau horaire approprié pour la personne. L'avertissement est parce que je fais un CROSS JOIN, et c'était entièrement intentionnel.

Avantages et inconvénients des autres méthodes de conversion de temps

Avant AT TIME ZONE, l'une de mes fonctionnalités préférées, mais souvent méconnue, de SQL 2008 était le type de données datetimeoffset. Cela permet également de stocker les données de date/heure avec le fuseau horaire, comme « 20160101 00:00 +10:30 », date à laquelle nous avons célébré le Nouvel An à Adélaïde cette année. Pour voir quand c'était dans US Eastern, je peux utiliser la fonction SWITCHOFFSET.

SELECT SWITCHOFFSET('20160101 00:00 +10:30', '-05:00'); -- 2015-12-31 08:30:00.0000000 -05:00

C'est le même moment dans le temps, mais dans une autre partie du monde. Si j'étais au téléphone avec quelqu'un en Caroline du Nord ou à New York, pour lui souhaiter une bonne année parce qu'il était juste minuit passé à Adélaïde, il dirait :« Qu'est-ce que tu veux dire ? C'est encore l'heure du petit-déjeuner ici le soir du Nouvel An !"

Le problème est que pour ce faire, j'ai besoin de savoir qu'en janvier, Adélaïde est à +10h30 et US Eastern à -5h00. Et c'est souvent pénible. Surtout si je parle de fin mars, début avril, octobre, début novembre - ces périodes de l'année où les gens ne peuvent pas être sûrs du fuseau horaire dans lequel se trouvaient les gens d'autres pays parce qu'ils changent d'une heure pour l'heure d'été, et ils le font tous selon des règles différentes. Mon ordinateur me dit dans quel fuseau horaire les gens se trouvent actuellement, mais il est beaucoup plus difficile de dire dans quel fuseau horaire ils se trouveront à d'autres moments de l'année.

Pour d'autres informations sur la conversion entre les fuseaux horaires et sur la manière dont des personnes comme Aaron ont géré l'heure d'été, consultez les liens suivants :

  • Utiliser AT TIME ZONE pour corriger un ancien rapport (c'est moi !)
  • Gérer la conversion entre les fuseaux horaires dans SQL Server – Partie 1
  • Gérer la conversion entre les fuseaux horaires dans SQL Server – Partie 2
  • Gérer la conversion entre les fuseaux horaires dans SQL Server – Partie 3

Et de la documentation officielle Microsoft :

  • AU FUSEAU HORAIRE (Transact-SQL)
  • sys.time_zone_info (Transact-SQL)
  • Aide et assistance pour l'heure d'été

Devez-vous utiliser AT TIME ZONE ?

AT TIME ZONE n'est pas parfait. Mais c'est vraiment utile - incroyablement. Il est suffisamment flexible pour accepter des colonnes et des variables en entrée, et je peux voir un énorme potentiel pour cela. Mais si cela doit entraîner la publication de mes estimations, je vais devoir faire attention. À des fins d'affichage, cela ne devrait pas avoir d'importance du tout, et c'est là que je peux voir qu'il est le plus utile. Faciliter la conversion d'une valeur d'affichage vers ou depuis l'UTC (ou vers ou depuis une heure locale), sans que personne n'ait à se casser la tête à propos des décalages et de l'heure d'été, est une grande victoire.

C'est vraiment l'une de mes fonctionnalités préférées de SQL Server 2016. Je réclame depuis très longtemps quelque chose comme ça.

Oh, et la plupart de ces milliards de personnes sur le fuseau horaire d'une demi-heure sont en Inde. Mais vous le saviez probablement déjà…