sql >> Base de Datos >  >> RDS >> Sqlserver

AT TIME ZONE:una nueva función favorita en SQL Server 2016


Imagen © Mark Boyle | Consejo del Día de Australia de NSW.
Imágenes propiedad de los respectivos artistas. Todos los derechos reservados.

AT TIME ZONE es una característica genial y no la había notado hasta hace poco, aunque Microsoft tiene una página al respecto desde diciembre.

Vivo en Adelaida en Australia. Y como más de mil millones de personas en el mundo, la gente de Adelaide tiene que lidiar con estar en una zona horaria de media hora. En horario de invierno, somos UTC+9:30, y en horario de verano, somos UTC+10:30. Excepto que si está leyendo esto en el hemisferio norte, deberá recordar que por "invierno" me refiero a abril a octubre. El horario de verano es de octubre a abril, y Santa Claus se sienta en la playa con una bebida fría, sudando a través de su grueso traje rojo y su barba. A menos que esté salvando vidas, por supuesto.

Dentro de Australia, tenemos tres zonas horarias principales (occidental, central y oriental), pero esto se extiende a cinco en el verano, ya que los tres estados que se extienden hasta el extremo norte de Australia (WA, Qld y NT) no tratar de salvar cualquier luz del día. Están lo suficientemente cerca del ecuador como para que no les importe, o algo así. Es muy divertido para el aeropuerto de Gold Coast, cuya pista cruza la frontera entre NSW y QLD.

Los servidores de bases de datos a menudo se ejecutan en UTC, porque simplemente es más fácil no tener que lidiar con la conversión entre UTC y local (y viceversa) en SQL Server. Hace muchos años, recuerdo haber tenido que corregir un informe que enumeraba los incidentes que ocurrían junto con los tiempos de respuesta (he escrito en un blog sobre esto desde entonces). Medir el SLA fue bastante sencillo:pude ver que ocurrió un incidente durante las horas de trabajo del cliente y que respondieron en una hora. Pude ver que otro incidente ocurrió fuera del horario laboral, y la respuesta fue en dos horas. El problema se produjo cuando se produjo un informe al final de un período en el que la zona horaria cambió, lo que provocó que un incidente que realmente ocurrió a las 5:30 p. m. (horario exterior) se muestre como si hubiera ocurrido a las 4:30 p. . La respuesta tomó alrededor de 90 minutos, lo cual estuvo bien, pero el informe mostraba lo contrario.

Todo esto está arreglado en SQL Server 2016.

Cómo usar AT TIME ZONE en SQL Server 2016

Ahora, con AT TIME ZONE, en lugar de decir:'20160101 00:00 +10:30', puedo comenzar con un valor de fecha y hora que no tiene un desplazamiento de zona horaria y usar AT TIME ZONE para explicar que está en Adelaida.

SELECT CONVERT(datetime,'20160101 00:00') 
    AT TIME ZONE 'Cen. Australia Standard Time';
 
-- 2016-01-01 00:00:00.000 +10:30

Y esto se puede convertir a la hora estadounidense agregando AT TIME ZONE nuevamente.

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

Ahora, sé que esto es mucho más prolijo. Y necesito convertir explícitamente la cadena a fecha y hora, para evitar un error que diga:

El tipo de datos del argumento varchar no es válido para el argumento 1 de la función AT TIME ZONE.

Pero a pesar de lo prolijo que es, me encanta, porque en ningún momento tuve que darme cuenta de que Adelaide estaba en +10:30, o que el Este era -5:00, simplemente necesitaba saber la zona horaria por su nombre. Me di cuenta de si se debía aplicar el horario de verano o no, y no tuve que hacer ninguna conversión de local a UTC para establecer una línea de base.

Funciona usando el registro de Windows, que tiene toda esa información, pero lamentablemente, no es perfecto cuando se mira hacia atrás en el tiempo. Australia cambió las fechas en 2008 y EE. UU. cambió sus fechas en 2005; ambos países reservaron la luz del día durante la mayor parte del año. AT TIME ZONE entiende esto. Pero no parece darse cuenta de que en Australia en el año 2000, gracias a los Juegos Olímpicos de Sydney, Australia comenzó a cambiar de horario unos dos meses antes. Esto es un poco frustrante, pero no es culpa de SQL, debemos culpar a Windows por eso. Supongo que el registro de Windows no recuerda la revisión que se realizó ese año. (Nota personal:es posible que deba pedirle a alguien del equipo de Windows que lo arregle...)

¡Sin embargo, la utilidad continúa!

Ese nombre de zona horaria ni siquiera necesita ser una constante. Puedo pasar variables e incluso usar columnas:

WITH PeopleAndTZs AS
(
  SELECT * FROM (VALUES 
    ('Rob',   'Cen. Australia Standard Time'),
    ('Paul',  'New Zealand Standard Time'),
    ('Aaron', 'US Eastern Standard Time')
  ) t (person, 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
*/

(Porque lo ejecuté justo antes de las 6:30 p. m. aquí en Adelaide, que resulta ser casi las 9 p. m. en Nueva Zelanda, donde se encuentra Paul, y casi las 5 a. m. de esta mañana en el este de Estados Unidos, donde se encuentra Aaron).

Esto me permitiría ver fácilmente qué hora es para las personas en cualquier lugar del mundo y ver quién sería el mejor para responder a algún problema, sin tener que realizar ninguna conversión manual de fecha y hora. Y más aún, me permitiría hacerlo por personas del pasado. Podría tener un informe que analice qué zonas horarias permitirían que ocurra la mayor cantidad de eventos durante el horario comercial.

Esas zonas horarias se enumeran en sys.time_zone_info , junto con la compensación actual y si se aplica actualmente el horario de verano.

nombre

desplazamiento_utc_actual

is_currently_dst
Hora estándar de Singapur

+08:00

0
W. Hora estándar de Australia

+08:00

0
Hora estándar de Taipei

+08:00

0
Hora estándar de Ulán Bator

+09:00

1
Hora estándar de Corea del Norte

+08:30

0
Aus Central W. Hora estándar

+08:45

0
Hora estándar de Transbaikal

+09:00

0
Hora estándar de Tokio

+09:00

0

Muestreo de filas de sys.time_zone_info

Solo estoy realmente interesado en cuál es el nombre, pero de todos modos. Y es interesante ver que hay una zona horaria llamada "Aus Central W. Standard Time" que está en el cuarto de hora. Imagínate. También vale la pena señalar que se hace referencia a los lugares usando su nombre de hora estándar, incluso si actualmente están observando el horario de verano. Como Ulaanbaatar en la lista anterior, que no figura como Ulaanbaatar Daylight Time. Esto puede hacer que las personas se vuelvan locas cuando comienzan a usar AT TIME ZONE.

¿AT TIME ZONE puede causar problemas de rendimiento?

Ahora, estoy seguro de que se está preguntando cuál podría ser el impacto de usar AT TIME ZONE en la indexación.

En cuanto a la forma del plan, no es diferente a tratar con la compensación de fecha y hora en general. Si tengo valores de fecha y hora, como en la columna de AdventureWorks Sales.SalesOrderHeader.OrderDate (sobre la cual creé un índice llamado rf_IXOD), entonces ejecuto esta consulta:

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

Y esta consulta:

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

En ambos casos, obtienes planes que se ven así:

Pero si exploramos un poco más de cerca, hay un problema.

El que usa AT TIME ZONE no usa muy bien las estadísticas. Cree que van a salir 5170 filas de ese Index Seek, cuando en realidad solo hay 217. ¿Por qué 5170? Bueno, la publicación reciente de Aaron, "Prestar atención a las estimaciones", lo explica, refiriéndose a la publicación "Estimación de cardinalidad para múltiples predicados" de Paul. 5170 es 31465 (filas en la tabla) * 0,3 * sqrt(0,3).

La segunda consulta lo hace bien, estimando 217. Verás, no hay funciones involucradas.

Esto es probablemente lo suficientemente justo. Quiero decir, en el momento en que está produciendo el plan, no le habrá pedido al registro la información que necesita, por lo que realmente no sabe cuánta estimar. Pero existe la posibilidad de que sea un problema.

Si agrego predicados adicionales que sé que no pueden ser un problema, entonces mis estimaciones en realidad se reducen aún más, hasta 89,9 filas.

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

Estimar demasiadas filas significa que se asigna demasiada memoria, pero estimar muy pocas puede causar muy poca memoria, con la necesidad potencial de un derrame para corregir el problema (que a menudo puede ser desastroso desde una perspectiva de rendimiento). Ve a leer la publicación de Aaron para obtener más información sobre cómo las estimaciones deficientes pueden ser malas.

Cuando considero cómo manejar la visualización de valores para esas personas de antes, puedo usar consultas como esta:

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

Y obtén este plan:

… que no tiene tales preocupaciones:el Compute Scalar más a la derecha está convirtiendo la fecha y hora OrderDate en datetimeoffset para UTC, y el Compute Scalar más a la izquierda lo está convirtiendo en la zona horaria adecuada para la persona. La advertencia es porque estoy haciendo una UNIÓN CRUZADA, y eso fue completamente intencional.

Ventajas y desventajas de otros métodos de conversión de tiempo

Antes de AT TIME ZONE, una de mis funciones favoritas, pero a menudo desapreciada, de SQL 2008 era el tipo de datos datetimeoffset. Esto permite que los datos de fecha/hora también se almacenen con la zona horaria, como '20160101 00:00 +10:30', que es cuando celebramos el Año Nuevo en Adelaida este año. Para ver cuándo fue en el este de EE. UU., puedo usar la función SWITCHOFFSET.

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

Este es el mismo momento en el tiempo, pero en una parte diferente del mundo. Si estuviera hablando por teléfono con alguien en Carolina del Norte o Nueva York, deseándole un feliz año nuevo porque era pasada la medianoche en Adelaida, estaría diciendo:"¿Qué quieres decir? ¡Todavía es la hora del desayuno aquí en la víspera de Año Nuevo!”

El problema es que para hacer esto, necesito saber que en enero, Adelaida es +10:30 y EE. UU. Este es -5:00. Y eso es a menudo un dolor. Especialmente si estoy preguntando sobre fines de marzo, principios de abril, octubre, principios de noviembre:esas épocas del año en las que las personas no pueden estar seguras de en qué zona horaria se encontraban las personas en otros países porque cambian una hora para el horario de verano, y todos lo hacen de acuerdo con diferentes reglas. Mi computadora me dice en qué zona horaria se encuentran las personas ahora, pero es mucho más difícil saber en qué zona horaria estarán en otras épocas del año.

Para obtener más información sobre la conversión entre zonas horarias y cómo personas como Aaron se han ocupado del horario de verano, consulte los siguientes enlaces:

  • Utilizar AT TIME ZONE para corregir un informe antiguo (¡soy yo!)
  • Manejar la conversión entre zonas horarias en SQL Server:parte 1
  • Manejar la conversión entre zonas horarias en SQL Server:parte 2
  • Manejar la conversión entre zonas horarias en SQL Server:parte 3

Y alguna documentación oficial de Microsoft:

  • EN LA ZONA HORARIA (Transact-SQL)
  • sys.time_zone_info (Transact-SQL)
  • Ayuda y apoyo para el horario de verano

¿Deberías usar AT TIME ZONE?

AT TIME ZONE no es perfecto. Pero es realmente útil, increíblemente. Es lo suficientemente flexible como para aceptar columnas y variables como entrada, y puedo ver un gran potencial para ello. Pero si va a hacer que mis estimaciones no sean válidas, entonces tendré que tener cuidado. Sin embargo, para fines de visualización, esto no debería importar en absoluto, y ahí es donde puedo ver que es más útil. Facilitar la conversión de un valor de visualización hacia o desde UTC (o hacia o desde una hora local), sin que nadie tenga que romperse la cabeza con las compensaciones y el horario de verano, es una gran victoria.

Esta es realmente una de mis funciones favoritas de SQL Server 2016. He estado pidiendo a gritos algo como esto durante mucho tiempo.

Ah, y la mayoría de esos mil millones de personas en la zona horaria de media hora están en India. Pero probablemente ya lo sabías...