Translate

quarta-feira, 26 de março de 2014

PostgreSql - Somar dias úteis a uma data

Vamos mostrar neste artigo, como utilizar a função "adiciona_dias_uteis" que adiciona dias úteis a uma data. Vamos apresentar três exemplos no final do artigo.

Não são considerados dias úteis:
  • Sábado ou domingo;
  • Feriados federais: feriados do país (nacional);
  • Feriados estaduais: feriados do estado;
  • Feriados municipais: feriados do munícipio (cidade);
  • Feriados móveis: feriados que não tem data fixa. Páscoa, Carnaval,  Sexta-feira da paixão e Corpus Christi;

Caso tenha interesse, faça o download do código dos exemplos comentados, ou veja no GitHub.
Após baixar os exemplos execute os scripts:
  • cria_tab_feriados_est_cid.sql: para criar as tabelas tab_feriado_federal, tab_feriado_estadual, tab_feriado_municipal, tab_cidades, tab_estados que são necessárias para executar o exemplo;
  • feriados_moveis.sql: cria a função feriados_moveis que calcula feriados sem data fixa. A função "feriados_moveis" é utilizada pela função "adiciona_dias_uteis";
  • adiciona_dias_uteis.sql: cria a função que adicionará dias úteis a uma data;
  • exemplo_1.sql, exemplo_2.sql e exemplo_3.sql: scripts dos exemplos 1, 2 e 3 respectivamente. Os scripts dos tópicos anteriores, devem ser executados antes dos scripts dos exemplos.

Para acrescentar dias úteis vamos utilizar:
  • tabela tab_feriado_federal: armazena os feriados federais no formato ano-mês-dia (yyyy-mm-dd). Pode parecer estranho a data estar com o ano de 1900, mas poderia ser qualquer outro ano, por exemplo, 2014. Quando verificamos se a data é feriado utilizamos apenas o mês e o dia, mas como o campo é do tipo date é obrigatório passar o dia, mês e ano. Veja a imagem a seguir.
  • tab_feriado_federal
    Descrição das colunas:
    • descricao: nome do feriado federal;
    • feriado_federal: data do feriado federal;
  • tabela tab_feriado_estadual: armazena os feriados estaduais. Os feriados estaduais também estão no formato (yyyy-mm-dd) e o ano também foi escolhido de forma aleatória, pois na verificação só é utilizado o mês e o dia. Veja a imagem a seguir.
  • tab_feriado_estadual
    Descrição das colunas:
    • sigla_estado: sigla do estado;
    • descricao: nome do feriado estadual;
    • feriado_estadual: data do feriado estadual;
  • tabela tab_feriado_municipal: armazena os feriados municipais. Os feriados municipais também estão no formato (yyyy-mm-dd) e o ano também foi escolhido de forma aleatória, pois na verificação só é utilizado o mês e o dia. Devido a quantidade de feriados municipais ser muito grande, preenchemos os feriados para as cidades de São Paulo e Porto Alegre, para servir como exemplo. Caso seja necessário, preencha a tabela com os feriados de outras cidades. Veja a imagem a seguir.
  • tab_feriado_municipal
    Descrição das colunas:
    • cidade_id: identificação da cidade;
    • descricao: nome do feriado;
    • feriado_municipal: data do feriado municipal;
  • tabela tab_cidades: armazena as cidades brasileiras. Cadastrei somente as cidades de São Paulo e Porto Alegre, para poder demonstrar o exemplo. Caso seja necessário, preencha a tabela com outras cidades.
  • tab_cidades
    Descrição das colunas:
    • cidade_id: identificação da cidade;
    • cidade_nome: nome da cidade;
    • sigla_estado: sigla do estado;
  • tabela tab_estados: armazena os estados brasileiros.
  • tab_estados
    Descrição das colunas:
    • sigla_estado: sigla do estado;
    • descrição: nome do estado;
  • função adiciona_dias_uteis: função executada pelo usuário, para utilizá-la devemos passar os argumentos a seguir:
  • SELECT adiciona_dias_uteis('data', quantidade_de_dias_uteis, codigo_da_cidade);
    Onde:
    • data: data inicial, ou seja a data a qual vai ser adicionado dias úteis;
    • quantidade_de_dias_uteis: indica quantos dias úteis é preciso somar a data;
    • codigo_da_cidade: código da cidade a qual estamos calculando os dias úteis. Os códigos das cidades utilizados na função devem estar armazenados na tabela "tab_cidades". Cadastrei somente as cidades de "São Paulo" e  "Porto Alegre" para demonstrar o exemplo. Cadastre novas cidades, conforme a sua necessidade. Caso as novas cidades tenham feriados municipais cadastre-os na tabela "tab_feriado_municipal";
  • função feriados_moveis: calcula os feriados móveis. Esta função não é executada pelo usuário. Ela é utilizada apenas pela função "adiciona_dias_uteis".


1º Exemplo

Adicionar 6 dias úteis a data 04/07/2014 para a cidade de São Paulo.


Solução

Vamos executar a função "adiciona_dias_úteis":

SELECT adiciona_dias_uteis('data'quantidade_de_dias_uteiscodigo_da_cidade);
Onde:
  • data: data inicial, ou seja a data a qual vai ser adicionado dias úteis;
  • quantidade_de_dias_uteis: indica quantos dias úteis é preciso somar a data;
  • codigo_da_cidade: codigo da cidade a qual estamos calculando os dias úteis; 

SELECT adiciona_dias_uteis('2014-07-04'61);


Após a execução da função, teremos o resultado a seguir.
2º Exemplo

Adicionar 6 dias úteis a data 04/07/2014 para a cidade de Porto Alegre.



Solução

Vamos executar a função "adiciona_dias_úteis":

SELECT adiciona_dias_uteis('data'quantidade_de_dias_uteiscodigo_da_cidade);
Onde:
  • data: data inicial, ou seja a data a qual vai ser adicionado dias úteis;
  • quantidade_de_dias_uteis: indica quantos dias úteis é preciso somar a data;
  • codigo_da_cidade: codigo da cidade a qual estamos calculando os dias úteis; 

SELECT adiciona_dias_uteis('2014-07-04'62);


Após a execução da função, teremos o resultado a seguir.
3º Exemplo

Os pacientes recebem os resultados dos exames 7 dias úteis após a data do exame. Devemos exibir as datas de entrega dos resultados dos exames. Para calcularmos as datas de entrega vamos utilizar as tabelas "tab_exame_paciente" e "tab_laboratorio". Veja as tabelas nas imagens a seguir:

tab_exame_paciente


Descrição das colunas da tabela tab_exame_paciente
  • exame_codigo: código do exame;
  • exame_data:  data do exame;
  • laboratorio_codigo: código do laboratório;
  • paciente_codigo: código do paciente;

tab_laboratorio

Descrição das colunas da tabela tab_laboratorio
  • laboratorio_codigo: código do laboratório;
  • laboratorio_endereco: endereço do laboratório;
  • cidade_id: identificação da cidade;
Solução

Vamos executar a função "adiciona_dias_úteis":

SELECT adiciona_dias_uteis('data'quantidade_de_dias_uteiscodigo_da_cidade);
Onde:
  • data: data inicial, ou seja a data a qual vai ser adicionado dias úteis. Vamos utilizar a coluna "exame_data" da tabela "tab_exame_paciente";
  • quantidade_de_dias_uteis: indica quantos dias úteis é preciso somar a data;
  • codigo_da_cidade: código da cidade a qual estamos calculando os dias úteis. Vamos utilizar a coluna "cidade_id" da tabela "tab_laboratorio";
Perceba que os parâmetros que serão utilizados na função são colunas que estão em tabelas diferentes, por isso vamos precisar utilizar o comando sql "JOIN".

SELECT
exame_codigo,
tab_exame_paciente.laboratorio_codigo,
paciente_codigo,
exame_data,
adiciona_dias_uteis(exame_data, 5, cidade_id) AS exame_entrega
FROM tab_exame_paciente
JOIN tab_laboratorio
ON tab_exame_paciente.laboratorio_codigo = tab_laboratorio.laboratorio_codigo;

Após a execução da função, teremos o resultado a seguir.


Comente no campo abaixo. Suas sugestões ou críticas serão bem-vindas.

quinta-feira, 20 de março de 2014

PostgreSql - Adicionar um intervalo a uma data / hora

Vamos mostrar neste artigo, como acrescentar um período a uma data. Descrevemos 6 exemplos:

Caso tenha interesse, faça o download do exemplo, ou veja no GitHub.
  • 1º exemplo: mostra como acrescentar um período em dias a uma data.
    São descritas duas soluções para resolver este problema. A primeira utiliza a soma de um número inteiro e a segunda utiliza a soma de um intervalo (interval);
  • 2º exemplo: mostra como acrescentar um período em meses a uma data;
  • 3º exemplo: mostra como acrescentar um período em anos a uma data;
  • 4º exemplo: mostra como acrescentar um período composto (utilizando mais de uma unidade de tempo).
  • 5º exemplo: mostra como acrescentar um período a uma coluna do tipo timestamp (possui data e hora).
  • 6º exemplo: mostra como acrescentar um período armazenado em uma coluna do tipo interval (possui intervalos) a uma coluna do tipo timestamp.
1º Exemplo

Os pacientes recebem os resultados dos exames 3 dias após a data do exame. Devemos exibir as datas de entrega dos resultados dos exames. Para calcularmos as datas de entrega vamos utilizar a tabela "tb_exame_paciente". Veja a tabela na imagem abaixo:


Descrição das colunas
  • paciente_codigo: código do paciente;
  • exame_codigo: código do exame;
  • exame_data: data do exame;
Temos duas formas para resolver este exemplo.

1ª Solução

Para acrescentarmos dias a uma data devemos somar um número inteiro.

data + n

Onde n: número de dias

Para exibir as datas de entrega do exame, vamos executar a consulta abaixo:

SELECT
paciente_codigo,
exame_codigo,
exame_data,
exame_data + 3 AS exame_entrega
FROM tb_exame_paciente;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.


Caso haja necessidade, formate as datas através do comando to_char. Veja o comando abaixo:

SELECT
paciente_codigo,
exame_codigo,
to_char (exame_data, 'dd/mm/yyyy') AS exame_data,
to_char (exame_data + 3, 'dd/mm/yyyy') AS exame_entrega
FROM tb_exame_paciente;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.


Caso queira saber mais sobre formatação de data veja o artigo:
PostgreSql - Formatar data

2ª Solução

Podemos somar um intervalo a data.

 data + inverval 'n período '
Onde:
  • inverval: tipo de dado que armazena um intervalo de tempo no PostgreSql;
  • n: valor numérico;
  • período: armazena unidades de medida de tempo. Elas são:
    • year / years: intervalo em anos. Ex.: 1 year, 2 years, 3 years ...
    • month / months / mon / mons: intervalo em meses. Ex.: 1 month, 2 months, 3 months ... Também podemos utilizar a unidade abreviada. Ex.: 1 mon, 2 mons, 3 mons ...
    • day / days: intervalo em dias. Ex.: 1 day, 2 days, 3 days ...
    • hour / hours: intervalo em horas. Ex.: 1 hour, 2 hours, 3 hours ...
    • minute / minutes / min / mins: intervalo em minutos. Ex.: 1 minute, 2 minutes, 3 minutes ... Também podemos utilizar a unidade abreviada. Ex.: 1 min, 2 mins, 3 mins ...
    • second / seconds / sec / secs: intervalo em segundos. Ex.: 1 second, 2 seconds, 3 seconds ... Também podemos utilizar a unidade abreviada. Ex.: 1 sec, 2 secs, 3 secs ...
    • millisecond / millisecond: intervalo em milissegundos. Ex.: 1 millisecond, 2 milliseconds, 3 milliseconds ...
    • microsecond / microseconds: intervalo em microssegundos. Ex.: 1 microsecond, 2 microseconds, 3 microseconds ...

Para resolver este exemplo vamos utilizar a unidade de tempo days, pois queremos acrescentar dias.

Para exibir as datas de entrega dos exame, vamos executar a consulta abaixo:

SELECT
paciente_codigo,
exame_codigo,
exame_data,
exame_data + interval '3 days' AS exame_entrega
FROM tb_exame_paciente;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.


Perceba que quando adicionamos um intervalo a uma data, será retornado além da data a hora (timestamp).

Caso haja necessidade, formate as datas através do comando to_char. Veja o comando abaixo:

SELECT
paciente_codigo,
exame_codigo,
to_char(exame_data, 'dd/mm/yyyy') AS exame_data,
to_char(exame_data + interval 'days' ,'dd/mm/yyyy') AS exame_entrega
FROM tb_exame_paciente;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.


2º Exemplo

Os equipamentos de uma fábrica passarão por avaliações a cada 3 meses a partir do início da utilização.
Serão feitas 2 avaliações. Para calcularmos as datas destas avaliações utilizaremos a tabela "tb_equipamentos". Veja a imagem desta tabela a seguir.

Descrição das colunas
  • equipamento_id: identificação do equipamento;
  • data_inicio: data de início da utilização do equipamento;
Solução

Podemos acrescentar um intervalo a data.

 data + inverval 'n período'

Onde:
  • inverval: tipo de dado que armazena um intervalo de tempo no PostgreSql;
  • n: valor numérico
  • período:  armazena a unidade de medida de tempo. Neste caso, vamos utilizar a unidade de tempo months, pois precisamos adicionar meses a coluna data_inicio;
Como a manutenção ocorre a cada 3 meses, a 1ª ocorrerá após 3 meses e a 2ª após 6 meses.
Para exibir as datas de manutenção dos equipamentos executamos a consulta abaixo:

SELECT
equipamento_id,
data_inicio,
data_inicio + interval '3 months' AS pri_manutencao,
data_inicio + interval '6 months' AS seg_manutencao
FROM tb_equipamentos;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.


Caso haja necessidade, formate as datas através do comando to_char. Veja o comando abaixo:

SELECT
equipamento_id,
to_char(data_inicio, 'dd/mm/yyyy') AS data_inicio,
to_char(data_inicio + interval '3 months', 'dd/mm/yyyy') AS pri_manutencao,
to_char(data_inicio + interval '6 months', 'dd/mm/yyyy') AS seg_manutencao
FROM tb_equipamentos;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.



3º Exemplo

As turmas de uma escola devem fazer 1 teste a cada ano, a partir da data de início das aulas. Serão feitos dois teste. Para calcularmos as datas dos testes utilizaremos a tabela "tb_turma". Veja a imagem desta tabela a seguir.


Descrição das colunas
  • turma_id: identificação da turma;
  • curso_id: identificação do curso;
  • data_inicio: data de início da turma;
Solução

Podemos acrescentar um intervalo a data.

 data + inverval 'n período'

Onde:
  • inverval: tipo de dado que armazena um intervalo de tempo no PostgreSql;
  • n: valor numérico;
  • período: armazena a unidade de medida de tempo. Neste caso, vamos utilizar a unidade de tempo year / years, pois precisamos adicionar anos a coluna data_inicio;
Como o teste é aplicado a cada ano,  o 1º teste ocorrerá após 1 ano e o 2º teste ocorrerá após 2 anos.

Para exibir as datas dos testes executamos a consulta abaixo:

SELECT
turma_id,
curso_id,
data_inicio,
data_inicio + interval '1 year' AS pri_teste,
data_inicio + interval '2 years' AS seg_teste
FROM tb_turma;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.


Caso haja necessidade, formate as datas através do comando to_char. Veja o comando abaixo:

SELECT
turma_id,
curso_id,
to_char(data_inicio, 'dd/mm/yyyy') AS data_inicio,
to_char(data_inicio + interval '1 year',  'dd/mm/yyyy') AS pri_teste,
to_char(data_inicio + interval '2 years', 'dd/mm/yyyy') AS seg_teste
FROM tb_turma;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.


4º Exemplo

Uma imobiliária faz contratos locação de imóveis por um período de 2 anos e 5 meses. Vamos calcular as datas de encerramento dos contratos. Para calcular as datas de encerramento dos contrato vamos utilizar a tabela "tb_locacao". Veja a imagem da tabela tb_locacao seguir.


Descrição das colunas
  • imovel_id: identificação do imóvel;
  • cliente_id: identificação do cliente;
  • data_inicio_locacao: data de início da locação;
Solução

Podemos acrescentar um intervalo a data.

 data + inverval 'n período'

Onde:
  • inverval: tipo de dado que armazena um intervalo de tempo no PostgreSql;
  • n: valor numérico;
  • período: armazena a unidade de medida de tempo. Neste caso, vamos utilizar duas unidades de tempo, years e months, pois precisamos adicionar anos e meses a coluna data_inicio_locacao;
Para exibir as datas de encerramento dos contratos executamos a consulta abaixo:

SELECT
imovel_id,
cliente_id,
data_inicio_locacao,
data_inicio_locacao + interval '2 years 5 months' AS data_fim_locacao
FROM tb_locacao;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.



Caso haja necessidade, formate as datas através do comando to_char. Veja o comando abaixo:

SELECT
imovel_id,
cliente_id,
to_char (data_inicio_locacao, 'dd/mm/yyyy'),
to_char (data_inicio_locacao + interval '2 year 5 months, 'dd/mm/yyyy') AS data_fim_locacao
FROM tb_locacao;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.

Observação

Perceba que foi adicionado um período de 2 anos e 5 meses (2 years 5 months). Também poderíamos adicionar um período de 29 meses (29 months) que retornaria o mesmo resultado. O objetivo deste exemplo é mostrar que é possível adicionar mais de uma unidade de tempo ao período.

5º Exemplo

Uma lanchonete quer que seja exibida em sua nota fiscal a data e o horário de previsão de entrega.
O prazo de entrega são de 40 minutos após a emissão do pedido.
Para exibir a data de previsão vamos utilizar a tabela "tb_pedido". Veja a tabela na imagem abaixo:


Descrição das colunas
  • pedido_codigo: código do pedido;
  • pedido_data: data do pedido;
Solução

Podemos acrescentar um intervalo a data e a hora.

 data_e_hora + inverval 'n período'

Onde:
  • inverval: tipo de dado que armazena um intervalo de tempo no PostgreSql;
  • n: valor numérico;
  • período: armazena a unidade de medida de tempo. Neste caso, vamos utilizar a unidade de tempo minutes, pois precisamos adicionar minutos a coluna pedido_data;
Para exibir a previsão do pedido executamos a consulta abaixo:

SELECT
pedido_codigo,
pedido_data,
pedido_data + interval '40 minutesAS data_entrega
FROM tb_pedido;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.


Caso haja necessidade, formate as datas através do comando to_char. Veja o comando abaixo:

SELECT
pedido_codigo,
to_char(pedido_data, 'dd/mm/yyyy hh24:mi:ss') AS pedido_data,
to_char(pedido_data + interval '40 minutes' , 'dd/mm/yyyy hh24:mi:ss') AS data_entrega
FROM tb_pedido;

Após a execução da sentença, será exibido o resultado. Veja na imagem a seguir.


Observação

Repare que utilizamos na mascara, hh24:mi:ss, para o horário, logo as horas serão formatadas no intervalo de (00-24) h, devido ao número 24 que acompanha a hora.
Se utilizarmos o formato a mascara hh:mi:ss, as horas serão formatadas no intervalo de (00-12) h.


6º Exemplo

Uma locadora de carros tem que calcular uma data de "devolução de veículos" a partir da data de "início da locação". Neste exemplo, os períodos de locação são variáveis de acordo com a solicitação do cliente.
Neste caso, foi necessário criar uma coluna para armazenar os períodos chamada "periodo".

Vamos utilizar a tabela "tb_loc_auto" para simular esta situação. Veja a imagem a seguir:



Segue o script de criação da tabela:

/*Comando para criar a tabela "tb_loc_auto"*/
CREATE TABLE tb_loc_auto
(
id            serial,
data_inicio timestamp,
periodo       interval,
CONSTRAINT pk_auto_id PRIMARY KEY(id)
);

/*
Comando para inserir múltiplos registros na tabela "tb_loc_auto".
*/
INSERT INTO tb_loc_auto (data_inicio, periodo)
VALUES
('2020-02-13 13:24:12', '2 years'),
('2020-03-14 19:30:15', '2 months'),
('2020-03-15 23:45:42', '2 days'),
('2020-03-16 23:59:30', '2 hours'),
('2020-03-17 23:59:30', '2 minutes'),
('2020-03-18 23:59:30', '2 seconds'),
('2020-03-19 23:59:30', '1 years 6 months 2 days 2 hours 2 minutes 2 seconds');

Para calcular as datas de devolução dos veículos executamos a consulta abaixo:

SELECT
id,
data_inicio,
periodo,
data_inicio + periodo AS data_devolucao
FROM tb_loc_auto;

Após a execução da sentença, será exibido o resultado na coluna "data_devolucao". Veja na imagem a seguir:


Deixe o seu comentário. Suas sugestões ou críticas serão bem-vindas.

segunda-feira, 10 de março de 2014

PostgreSql - mudar o nome de uma tabela

Para renomear uma tabela no PostgreSql utilizamos o seguinte comando:
ALTER TABLE nome_da_tabela RENAME TO novo_nome_da_tabela;
Caso tenha interesse, faça o download do exemplo, veja no GitHub ou assista o vídeo no final do artigo.

1º Exemplo

Vamos alterar o nome da tabela "tb_empregados" para "tb_funcionarios". Veja a imagem da tabela "tb_empregados", a seguir:
Para alterar o nome desta tabela executamos a sentença a seguir:
ALTER TABLE tb_empregados RENAME TO tb_funcionarios ;
Observações

O usuário que vai fazer a alteração do nome da tabela deve ser um super usuário, por exemplo, postgres ou proprietário da tabela. Se o usuário não satisfazer pelo menos uma dessas condições será exibido o seguinte erro:

ERRO: permissão negada para relação nome_da_tabela. (Português)

OU

ERROR: must be owner of relation nome_da_tabela (Inglês).

Este erro indica que o usuário não é proprietário da tabela e não pode alterar o nome dela.

Para corrigir erro deve-se alterar o usuário ou grupo proprietário da tabela, veja o comando a seguir:

Exemplo

ALTER TABLE nome_da_tabela OWNER TO nome_do_usuario;

/*Alterar o usuário*/
ALTER TABLE tb_empregados OWNER TO paulo;

OU

ALTER TABLE nome_da_tabela OWNER TO nome_do_grupo;

/*Alterar do grupo*/
ALTER TABLE tb_empregados OWNER TO rh;



Veja também:

PostgreSql - Mudar o nome de uma coluna