- 3. SQL Server, Datas e Horas: Tipos de dados
- 2. SQL Server, Datas e Horas: Exibição e Conversões
- 1. SQL Server, Datas e Horas
Olá! No último post, focamos em como o SQL Server entende as datas que enviamos no código T-SQL. Para resumir: Especificar datas no SQL Server é como especificar uma string qualquer, porém com um formato específico. Você pode usar o DATEFORMAT para auxiliar na interpretação do SQL Server (e não como a data é exibida) ou usar formatos genéricos, como o ISO. Bom, agora vamos falar sobre a exibição das datas, ou seja, o formato de saída!
Vamos começar alinhando um ponto: O SQL Server não exibe nada! Ele entrega data à alguma aplicação (algum driver de conexão como jTDS, ODBC,OLEDB, etc.). Internamente, uma data é bem diferente do formato como especificamos. A data não é armazenada como texto. É um formato binário bem diferente: imagine que seja um número muito louco que de alguma forma contenha os dias, meses, anos, hora, etc. (prometo um post sobre isso em breve). Ao efetuar um SELECT que retorne uma coluna com algum, por exemplo, datetime, o SQL Server não entrega uma string para a aplicação. Ele entrega este número (binário). A aplicação é responsável por interpretar este valor e exibi-lo da forma correta, ou seja, a aplicação também entende onde está o dia, mês, ano e horário nesse número. (O excel também trabalha de forma parecida. Experimente digitar este valor 42071,1588859 e formatar a célula como data…)
No caso do Management Studio (SSMS), quando você visualiza os resultado em um GRID (padrão), ele vai exibir as datas no formato ‘YYYY-MM-AA HH:MM:SS.mmm’. Dependendo do tipo de dado (date, time, datetime2, etc.) ele vai omitir a data ou o horário. Perceba (execute no SSMS):
SELECT CONVERT(datetime,'20150328 15:02'); SELECT CONVERT(date,'20150328 15:02');
A questão aqui é: a aplicação é a verdadeira responsável por exibir a data em um formato mais apropriado.
Caso você não queria que a aplicação faça estre trabalho você pode forçar o SQL Server a entregar uma data como uma string, ao invés deste número binário maluco… Em outras palavras, você pode converter uma data para uma string. Bom aqui cabe deixar uma coisa clara: quando você converte uma data para string, aquela data perde seus poderes mágicos: comparar com outras datas, adicionar datas, subtrair, ordenar. A data convertida passa a ser tratada como texto puro e perde certos comportamentos exclusivos de data. Se sua aplicação não vai precisar realizar nenhum processamento extra com aquela data e se o seu objetivo é apenas exibição, então não há problemas! Observe os exemplos a seguir para entender melhor. Para converter uma data para string você usa a função CONVERT. Ela permite que você especifique um parâmetro especial chamado Style (A função CAST também pode ser usada, mas não permite especificar o Style). O Style é um número pré-determinado que faz com que o resultado da conversão saia em um formato específico. A sintaxe geral é:
CONVERT(varchar(tamanho), ValorData, Style)
Primeiro, você deve estar atento ao tamanho da string (pode ser varchar ou char) que irá receber o resultado. Se o seu objetivo é converter a data para o formato dd/MM/AAAA então você vai precisar de 2 caracteres para o dia, 2 para o mês, 4 para o ano e mais 2 para as barras, totalizando 10 caracteres. Mas se você quiser incluir as horas e minutos (dd/MM/AAAA HH:MM) então, além dos 10, você vai precisar de 1 para espaço, 2 para as horas, 1 para os dois-pontos e 2 para os minutos, isto é, mais 6 caracteres, o que faz necessário 16 caracteres para a data inteira. Então fique atento a isso para colocar o tamanho correto, ou você irá ver sua data cortada… O Style é um valor fixo e pré-determinado que você pode encontrar aqui na documentação desta função. ValorData pode ser qualquer coisa que seja uma data. Em outras palavras, ValorData pode ser qualquer expressão que resulte em um tipo de data: funções de sistema (DATEADD,CURRENT_TIMESTAMP, etc.), funções de usuário, nomes de variável, nomes de colunas, etc. Bom, a documentação é sua melhor fonte. Este site também contém uma série de exemplos de uso da função e irá te ajudar quando precisar de uma resposta rápida. No mais, alguns exemplos:
DECLARE @UmaDataQualquer datetime SET @UmaDataQualquer = CURRENT_TIMESTAMP SELECT @UmaDataQualquer AS [datetime] ,CONVERT(varchar(10),@UmaDataQualquer,103) AS [varchar(10)] -- Podemos converter qualquer formato de data para texto ,CONVERT(varchar(6),@UmaDataQualquer,103) AS [varchar(6)] -- Se você colocar o tamanho incorreto, poderá ter ver os dados cortados -- O dateformat não tem nenhum efeito sobre a saída SET DATEFORMAT dmy; SELECT @UmaDataQualquer [datetime],CONVERT(varchar(10),@UmaDataQualquer,103) AS [Texto,DATEFORMAT = dmy] SET DATEFORMAT mdy; SELECT @UmaDataQualquer [datetime],CONVERT(varchar(10),@UmaDataQualquer,103) AS [Texto,DATEFORMAT = mdy]
Uma coisa interessante é que podemos notar o que foi falado aqui com o uso do comando DBCC OUTPUTBUFFER. Basicamente, este comando nos permite ver os dados que uma determinada sessão entregou para a aplicação. Execute esta query:
DECLARE @UmaDataQualquer datetime SET @UmaDataQualquer = CURRENT_TIMESTAMP select 'ANTES_DA_DATA',CONVERT(varchar(10),@UmaDataQualquer,103),'DEPOIS_DA_DATA'; select 'ANTES_DA_DATA',@UmaDataQualquer,'DEPOIS_DA_DATA';
Agora abra outra sessão e execute o comando DBCC OUTPUTBUFFER, passando como parâmetro o número da sessão onde a query anterior foi executada (no meu caso, foi 51):
-- Você deverá ser sysadmin para executar este comando. -- Substitua 51 pelo número da sessão desejado DBCC OUTPUTBUFFER(51)
Você deverá um resultado parecido com este (irei manter somente o início, que é o relevante pra nós neste momento):
00000040 04 f0 00 36 00 d1 0d 00 41 4e 54 45 53 5f 44 41 .ð.6.Ñ..ANTES_DA
00000050 5f 44 41 54 41 0a 00 32 38 2f 30 33 2f 32 30 31 _DATA..28/03/201
00000060 35 0e 00 44 45 50 4f 49 53 5f 44 41 5f 44 41 54 5..DEPOIS_DA_DAT
00000070 41 fd 11 00 c1 00 01 00 00 00 00 00 00 00 81 03 Aý..Á………..
00000080 00 00 00 00 00 20 00 a7 0d 00 09 04 f0 00 36 00 ….. .§.. .ð.6.
00000090 00 00 00 00 21 00 6f 08 00 00 00 00 00 20 00 a7 ….!.o…… .§
000000a0 0e 00 09 04 f0 00 36 00 d1 0d 00 41 4e 54 45 53 .. .ð.6.Ñ..ANTES
000000b0 5f 44 41 5f 44 41 54 41 08 69 a4 00 00 d9 73 0e _DA_DATA.i¤..Ùs.
000000c0 01 0e 00 44 45 50 4f 49 53 5f 44 41 5f 44 41 54 …DEPOIS_DA_DAT
Primeiro, repare no trecho em verde. Este é o trecho que corresponde ao primeiro SELECT, onde nós usamos um CONVERT para converter a data para texto. Perceba que o valor retornado é realmente um texto puro, e por isso podemos visualizar facilmente os caracteres correspondentes. Agora, repare o trecho em azul. Este trecho corresponde ao segundo SELECT, onde nós retornamos a data pura. Notou!? Cadê a data? Ela está ali, só que está sendo entregue em um formato binário, cujo seus bytes não são equivalentes aos digitos e barras, conforme no trecho anterior. O trecho em binário que corresponde à data está marcado em laranja, apenas por curiosidade (no post que eu prometi sobre data, eu darei mais detalhes 🙂 ).
Para finalizar esta parte, um último ponto: Os poderes mágicos da data. Uma das grandes vantagens em ter tipos de dados que trabalhem exclusivamente com datas é a facilidade que temos em lidar com operações aritméticas envolvendo datas, como por exemplo, somar dias, meses, horas, minutos e segundo. Assim, o SQL Server é quem se preocupa em fazer todas as verificações quando, por exemplo, você adiciona 1 minuto à data “28/02/2016 23:59:59.003”. Ordenar datas também é um diferencial: Por exemplo, considere as datas ’10/02/2015′ e ’01/03/2015′. Claramente, se eu te pedisse para ordenar estas datas de forma crescente, você, uma pessoa muito esperta, me diria assim:
’10/02/2015′
’01/03/2015′
Que está correto! O SQL Server também faria exatamente a mesma coisa, se estes valores estiverem sendo trabalhados como data… Agora olha só o que acontece quando você converte para texto e ordena pelo resultado da conversão:
DECLARE @MinhasDatas TABLE(data datetime); INSERT INTO @MinhasDatas VALUES('20150210'); INSERT INTO @MinhasDatas VALUES('20150301'); SELECT data as OrdenadoPorData FROM @MinhasDatas ORDER BY data ASC-- Aqui está ok! SELECT data as OrdenadoPorTexto FROM @MinhasDatas ORDER BY CONVERT(varchar(10),data,103) ASC -- Ops!
Aqui, os resultados:
OrdenadoPorData
2015-02-10 00:00:00.000
2015-03-01 00:00:00.000
OrdenadoPorTexto
2015-03-01 00:00:00.000
2015-02-10 00:00:00.000
Repare que no segundo SELECT, a data de março veio ANTES da data de fevereiro, em um ORDER BY crescente 😯 ! Será um bug? Nada disso. Quando você converteu para texto e ordenou pelo resultado em texto, as regras de ordenação mudaram, pois para texto são diferentes das regras para data. Veja:
DECLARE @MinhasDatas TABLE(data datetime); INSERT INTO @MinhasDatas VALUES('20150210'); INSERT INTO @MinhasDatas VALUES('20150301'); SELECT data as OrdenadoPorTexto,CONVERT(varchar(10),data,103) as Convertido FROM @MinhasDatas ORDER BY CONVERT(varchar(10),data,103) ASC
Resultados:
OrdenadoPorTexto Convertido
2015-03-01 00:00:00.000 01/03/2015
2015-02-10 00:00:00.000 10/02/2015
O motivo é simples: A string ’01/03/2015′ começa com ‘0’ (dígito zero), que conforme as regras de ordenação de texto (onde envolve collations e outras coisas) é menor do que ‘1’.
Entendeu o porquê deve-se tomar cuidado com as conversões de data para texto? No próximo post, último desta série, vamos falar sobre os tipos de dados… Aproveitando o gancho, você saberia explicar o porquê disto?
SELECT D.DataString ,CONVERT(datetime,D.DataString) AS DataDatetime FROM ( VALUES ('20150101 23:59:59.002') ,('20150101 23:59:59.004') ,('20150101 23:59:59.005') ,('20150101 23:59:59.006') ,('20150101 23:59:59.009') ,('20150101 23:59:59.011') ,('20150101 23:59:59.012') ) D(DataString)
Se não, aguarde o próximo post (Que tentarei publicar na semana que vem).
[]’sDBA Team Leader na Power Tuning
Comments ( 2 )