Olá,
estou com um sql bruta que fiz e não encontro uma forma melhor ou otimizada para resolver meu problema. Contexto: tenho uma tabela TAREFA e uma tabela TAREFA_STATUS que armazena data_hora e o status da tarefa.
Oque eu preciso é de um status diário de quantas tarefas se encontram em cada status. O resultado da sql que fiz me traz isto, porém muito lento. SQL:
Minha ideia era essa, estava tentando desta forma, mas não fecha, pq pode acontecer de ter várias trocas de status no mesmo dia e preciso me basear no último status daquela tarefa para aquela data.
Por isso uso sempre este trecho abaixo para traze o último status da tarefa:
Como basicamente cada tarefa passa por cada status, sem isso ele contabiliza todas para o primeiro status, pois todas já passaram ou estão nele ainda, e na verdade eu preciso saber quantas AINDA ESTÃO no primeiro status e nos seguintes, ou seja, contabilizar pelo status atual.
Eu já tinha conseguido fazer mas com auxílio de software, percorrendo o intervalo de datas e ai buscando os valores. Fico mais complicado colocando o intervalo na busca.
L
LostSoldier
Usando um MAX na data não resolve o caso do último status?
Vi na doc do postgresql (é esse que você está usando certo?) a função extract
R
Rodrigo_Void
Não, to usando cast pra date, pq meu campo é timestamp.
Não entendi este having, deve retornar boolean (comparação por exemplo). Não aceita HAVING MAX(extract(hour from data))
L
LostSoldier
Mas a função extract usa justamente um timestamp…
Quanto ao having, fiz meio que chute, o mysql aceita coisas desse tipo… enfim, se colocar o max junto ao select?
Obs: o having serve para fazer um filtro enquanto agrupa, minha lógica seria para não exibir uma coluna extra contendo a maior hora…
R
Rodrigo_Void
Ainda não dá, ele sempre traz o mesmo valor pra cada status.
Também devo considerar que o último status pode não ter sido definido naquela data e sim dias antes, porém é ele que vale por ser o mais atual. Exemplo: para contabilizar a quantidade de tarefas por status, devo usar o status mais recente de cada tarefa para o dia o qual vou contabilizar. No caso daquele intervalo de data, preciso a contagem de cada status para cada dia, assim como no resultado que postei, mas cuidando que ocorrem tarefas que tiveram a ultima atualização de status antes daquele intervalo. E conta junto, pois é o mais recente.
L
LostSoldier
E agrupando também pelo id_tarefa com ordenação desc pela data?
GROUPBYdata,id_tarefaORDERBYdataDESC
Vai trazer os últimos primeiro, mas traria a maior hora, se é isso que você quer…
Não há como fazer somente com uma select. O status mais recente pode estar fora do intervalo que busco, ai a tarefa ficaria sem status.
EX: para contabilizar a quantidade de tarefas por status, devo usar o status mais recente de cada tarefa para o dia o qual vou contabilizar. No caso daquele intervalo de data, preciso a contagem de cada status para cada dia, assim como no resultado que postei, mas cuidando que ocorrem tarefas que tiveram a ultima atualização de status antes daquele intervalo. E conta junto, pois é o mais recente.
R
Rodrigo_Void
Para evitar uma consulta para cada coluna, tentei via case numa consulta só, mas dentro do contexto de intervalo de datas não rola.
selects.data,sss.*from(selectcast(s.data_horaasDATE)asdatafromtarefa_statusswherecast(s.data_horaasDATE)between'2017-03-01'and'2017-03-30'groupby1)sjoin(selectcount(casess.statuswhen1then1elsenullend)ass1,count(casess.statuswhen2then1elsenullend)ass2,count(casess.statuswhen3then1elsenullend)ass3,count(casess.statuswhen4then1elsenullend)ass4,count(casess.statuswhen5then1elsenullend)ass5,count(casess.statuswhen6then1elsenullend)ass6,count(casess.statuswhen7then1elsenullend)ass7from(selectDISTINCTon(1)ts.id_tarefa,ts.id,ts.status,ts.data_horafromtarefa_statustswherecast(ts.data_horaasDATE)<=s.data-- n funcionaorderby1,4desc,2desc)ssgroupby1orderby1)ssson(???????????)orderby1
Não sei como resolver este join nem como poderia usar a data da consulta principal para a contabilização dentro do join. Alí que parei.
L
LostSoldier
Eu entendi seu problema… mas usando um or, por causa do status recente que não cai no between…
Afinal pelo que entendi, se o status da tarefa mudou hoje, ele tem de entrar certo?
Rapaz, vou ser sincero, ver subquery me dá agonia
Ideal seria matar esse problema com uma query só e um join sem subquery
R
Rodrigo_Void
Da forma abaixo, até executa, só não pega o último status para cada dia, tá pegando o último geral:
selects.data,sss.*from(selectcast(s.data_horaasDATE)asdatafromtarefa_statusswherecast(s.data_horaasDATE)between'2017-03-01'and'2017-03-30'groupby1)sjoin(selectcast(ss.data_horaasDATE)asdata,count(casess.statuswhen1then1elsenullend)ass1,count(casess.statuswhen2then1elsenullend)ass2,count(casess.statuswhen3then1elsenullend)ass3,count(casess.statuswhen4then1elsenullend)ass4,count(casess.statuswhen5then1elsenullend)ass5,count(casess.statuswhen6then1elsenullend)ass6,count(casess.statuswhen7then1elsenullend)ass7from(selectDISTINCTon(1)ts.id_tarefa,ts.id,ts.status,ts.data_horafromtarefa_statusts--where cast(ts.data_hora as DATE) <= s.data --s.dataorderby1,4desc,2desc)ssgroupby1orderby1)ssson(sss.data=s.data)orderby1
L
LostSoldier
ssson(???????????)
Seria no caso?
ss.id=ts.id_tarefa
R
Rodrigo_Void
Certo, mas não pode ser menor que AGORA pq devo considerar o status em que a tarefa se encontrada naquele determinado dia, não depois. Ex.: Em 25/12/1988 qual era o status da tarefa x? Ele pode ter sido setado em 1950, mas é ele que vale, se tiver outro status de 26/12/1988 deve ser desconsiderado.
L
LostSoldier
Agora complicou tudo , você acabou de dizer que precisa pegar o status mais recente, nesse seu exemplo não seria o de 1988?
Eu coloquei o agora (now()) imaginando que você ainda não quer listar status futuros, até porque creio que nem foram lançados…
R
Rodrigo_Void
rsrs, não. em 25/12/1988 não tem status, é o dia que estou usando para consultar.
Vamos para mais um exemplo
Tarefa 1
Status da tarefa 1:
Tarefa 1 - 05/01/2017 - status 1
Tarefa 1 - 20/01/2017 - status 2
Tarefa 1 - 15/02/2017 - status 3
Qual é o status da tarefa 1 no dia 01/02/2017. É 2. Não é 3, pq 3 já seria no “futuro”, já que se estivessemos em 01/02/2017 o status atual dela seria 2, ela não teria ainda o status 3 pq isso ainda não aconteceu.
Isso é um histórico pra mim, preciso de uma espécie de regressão. Analisando cada data de lá pra tráz. O hj não importa, n deve ser considerado.
Agora ele me permite usar aquele campo data dentro do join. Só que ele não me traz os valores corretos, acredito ser culpa do group da data do join. Está contanto qt de trocas daquele status naquele dia.
Preciso tirar a coluna de data que está junto com os count, tiro o group by também, mas daí não consigo jazer o ON do join.
R
Rodrigo_Void
A sql abaixo me resolveu 100%:
selects.data,sss.*from(selectcast(s.data_horaasDATE)asdatafromtarefa_statusswherecast(s.data_horaasDATE)between'2017-03-01'and'2017-03-30'groupby1)sjoinLATERAL(select--cast(ss.data_hora as DATE) as data,count(casess.statuswhen1then1elsenullend)ass1,count(casess.statuswhen2then1elsenullend)ass2,count(casess.statuswhen3then1elsenullend)ass3,count(casess.statuswhen4then1elsenullend)ass4,count(casess.statuswhen5then1elsenullend)ass5,count(casess.statuswhen6then1elsenullend)ass6,count(casess.statuswhen7then1elsenullend)ass7from(selectDISTINCTon(1)ts.id_tarefa,ts.id,ts.status,ts.data_horafromtarefa_statustswherecast(ts.data_horaasDATE)<=s.data--'2017-03-01'orderby1,4desc,2desc)ss--group by 1--order by 1)ssson1=1--(sss.data = s.data)orderby1
POREM, tive q usar aquele 1=1 pra juntar as consultas, mas me traz os mesmos resultados que a primeira sql que tinhas subconsultas nas colunas. Agora de 24 segundos, passou para 4 segundos. Poderia ser melhor…
A
AbelBueno1 like
Eu nao tenho certeza se eu entendi seu problema corretamente. Se desse uns exemplos de dados existentes e o resultado esperado, ficaria mais fácil tentar uma soluçao equivalente mais simples e performática.
Você poderia mostrar seu esquema aqui por exemplo: http://sqlfiddle.com/
Com relaçao a performance que você tem agora, se nao encontrar uma query mais simples, o próximo passo é analisar o plano de execuçao, ver se precisa criar indíces, etc.
Quantos registros existem na tabela no total? Quantos serao geralmente usados nessa query?