Eu tenho essa mesa
Código de Máquina | DoCreation | CódigoDeEstadoDaMáquina |
---|---|---|
DM139 | 2024-04-03 00:32:17.377 | 99 |
DM139 | 2024-04-03 00:32:49.080 | 0 |
DM139 | 2024-04-03 01:51:14.427 | 99 |
DM139 | 2024-04-03 01:51:45.643 | 0 |
DM139 | 2024-04-03 06:07:38.357 | 3 |
DM139 | 2024-04-03 06:07:39.043 | 0 |
DM139 | 2024-04-03 06:23:37.433 | 100 |
DM139 | 2024-04-03 06:23:43.697 | 0 |
DM139 | 2024-04-03 06:24:46.153 | 1 |
DM139 | 2024-04-03 06:25:02.467 | 0 |
DM139 | 2024-04-03 06:46:50.410 | 100 |
DM139 | 2024-04-03 06:47:29.247 | 0 |
DM139 | 2024-04-03 06:47:46.397 | 100 |
DM139 | 2024-04-03 06:53:47.160 | 0 |
DM139 | 2024-04-03 06:59:41.633 | 1 |
DM139 | 2024-04-03 06:59:54.550 | 0 |
DM139 | 2024-04-03 07:00:03.203 | 1 |
DM139 | 2024-04-03 07:00:06.437 | 0 |
DM139 | 2024-04-03 07:00:14.247 | 1 |
DM139 | 2024-04-03 07:00:17.683 | 2 |
DM139 | 2024-04-03 07:00:18.153 | 1 |
DM139 | 2024-04-03 07:00:18.840 | 2 |
DM139 | 2024-04-03 07:00:26.860 | 1 |
DM139 | 2024-04-03 07:00:46.387 | 2 |
cada registro representa uma "mudança de estado" da máquina específica DM139 onde
- MachineCode = código da máquina (nvarchar)
- DoCreation = registro de data e hora de quando a máquina mudou seu estado (data e hora)
- MachineStateCode = novo estado da máquina, onde 99 = reinicializando, 0 = inicializando, 3 = erro, 1 = iniciando, 2 = funcionando e assim por diante (int)
Preciso criar uma nova coluna, vamos chamá-la de MachineStateCodeAdjusted, onde seu valor é igual a 3 se o MachineStateCode anterior for 3 e o MachineStateCode atual não for 2, caso contrário, deve ter o valor atual do MachineStateCode. (a próxima tabela é o que preciso)
Código de Máquina | DoCreation | CódigoDeEstadoDaMáquina | MachineStateCodeAjustado |
---|---|---|---|
DM139 | 2024-04-03 00:32:17.377 | 99 | 99 |
DM139 | 2024-04-03 00:32:49.080 | 0 | 0 |
DM139 | 2024-04-03 01:51:14.427 | 99 | 99 |
DM139 | 2024-04-03 01:51:45.643 | 0 | 0 |
DM139 | 2024-04-03 06:07:38.357 | 3 | 3 |
DM139 | 2024-04-03 06:07:39.043 | 0 | 3 |
DM139 | 2024-04-03 06:23:37.433 | 100 | 3 |
DM139 | 2024-04-03 06:23:43.697 | 0 | 3 |
DM139 | 2024-04-03 06:24:46.153 | 1 | 3 |
DM139 | 2024-04-03 06:25:02.467 | 0 | 3 |
DM139 | 2024-04-03 06:46:50.410 | 100 | 3 |
DM139 | 2024-04-03 06:47:29.247 | 0 | 3 |
DM139 | 2024-04-03 06:47:46.397 | 100 | 3 |
DM139 | 2024-04-03 06:53:47.160 | 0 | 3 |
DM139 | 2024-04-03 06:59:41.633 | 1 | 3 |
DM139 | 2024-04-03 06:59:54.550 | 0 | 3 |
DM139 | 2024-04-03 07:00:03.203 | 1 | 3 |
DM139 | 2024-04-03 07:00:06.437 | 0 | 3 |
DM139 | 2024-04-03 07:00:14.247 | 1 | 3 |
DM139 | 2024-04-03 07:00:17.683 | 2 | 2 |
DM139 | 2024-04-03 07:00:18.153 | 1 | 1 |
DM139 | 2024-04-03 07:00:18.840 | 2 | 2 |
DM139 | 2024-04-03 07:00:26.860 | 1 | 1 |
DM139 | 2024-04-03 07:00:46.387 | 2 | 2 |
Usei a função LAG para obter o MachineStateCode anterior (e funciona), mas para funcionar como eu quero, preciso obter o MachineStateCodeAdjusted anterior, que não é acessível para a função LAG.
A consulta SQL que estou executando é esta:
SELECT MachineCode
, DoCreation
, MachineStateCode
, IIF(LAG(MachineStateCode, 1, MachineStateCode) OVER (ORDER BY DoCreation ASC) = 3
AND MachineStateCode<>2, 3, MachineStateCode) AS MachineStateCodeAdjusted
FROM mch.MachineStateChanges
e o resultado que obtive me mostra claramente que não funciona como eu quero
Código de Máquina | DoCreation | CódigoDeEstadoDaMáquina | MachineStateCodeAjustado |
---|---|---|---|
DM139 | 2024-04-03 00:32:17.377 | 99 | 99 |
DM139 | 2024-04-03 00:32:49.080 | 0 | 0 |
DM139 | 2024-04-03 01:51:14.427 | 99 | 99 |
DM139 | 2024-04-03 01:51:45.643 | 0 | 0 |
DM139 | 2024-04-03 06:07:38.357 | 3 | 3 |
DM139 | 2024-04-03 06:07:39.043 | 0 | 3 |
DM139 | 2024-04-03 06:23:37.433 | 100 | 100 ERRADOS |
DM139 | 2024-04-03 06:23:43.697 | 0 | 0 ERRADO |
DM139 | 2024-04-03 06:24:46.153 | 1 | 1 ERRADO |
Também tentei com um CTE recursivo e funciona, mas é muito lento! A extração da consulta leva 4 minutos e 2 dias de trabalho, enquanto eu preciso extrair 1 ano!
Qual é a melhor (e rápida) abordagem para consertar isso?
Acho que sua explicação está incompleta. Você deveria ter indicado que, uma vez obtido um "erro" (3), todos os estados não importam, até que se obtenha um "funcionando" (2) novamente.
Neste caso, trata-se de um problema de Lacunas e Ilhas. Você pode usar a solução tradicional:
Resultado:
Veja o exemplo em execução em db<>fiddle .
Provavelmente você deseja aplicar as seguintes regras para a nova coluna
MachineStateCodeAdjusted
:Se MachineStateCode for 2 ou 3, copie-o
Caso contrário, encontre a primeira linha antes da linha atual (classificada pela data) cujo MachineStateCode é 2 ou 3. Se esta linha anterior tiver o código 2 ou se nenhuma linha anterior puder ser encontrada, copie o MachineStateCode da linha atual; caso contrário, pegue 3.
Uma maneira de implementar essa lógica é esta consulta:
A ideia por trás:
Na consulta "sub" (renomeie-a se quiser e souber um nome melhor), todas as linhas com MachineStateCode 2 ou 3 serão selecionadas, seu MachineStateCode será simplesmente copiado como MachineStateCodeAdjusted.
A consulta principal seleciona todas as linhas cujo MachineStateCode não é 2 ou 3. Para essas linhas, será verificado se existem linhas anteriores (com uma data anterior) com MachineStateCode 2 ou 3.
Se tais linhas existirem, pegue a primeira (por data) antes da linha atual e verifique se essa linha tinha MachineStateCode 2 ou 3. Se fosse 2, copie o MachineStateCode, caso contrário, pegue 3 como MachineStateCodeAdjusted.
Se não existirem tais linhas, basta copiar o MachineStateCode como MachineStateCodeAdjusted.
Classifique todo o resultado por MachineCode e data
Observe que talvez seja necessário estender a consulta se você quiser criar mais casos ou fazer com que a lógica e o resultado dependam do MachineCode atual (primeira coluna). Em seguida, você pode, por exemplo, adicionar uma condição para que o MachineCode na consulta principal e na subconsulta corresponda, se for o caso.
Veja este db<>fiddle com seus dados de exemplo.
No SQL Server 2022 em diante (ou no Azure), você pode usar
IGNORE NULLS
comLAST_VALUE()
(assim como o RedShift) .Isso significa que você pode ler facilmente o último código 2 ou 3 e usá-lo em uma expressão CASE.
violino