Obtenha o último item de uma tabela - SQL
-
09-06-2019 - |
Pergunta
Eu tenho uma tabela de histórico no SQL Server que basicamente rastreia um item por meio de um processo.O item possui alguns campos fixos que não mudam ao longo do processo, mas possui alguns outros campos, incluindo status e ID, que aumentam à medida que as etapas do processo aumentam.
Basicamente, quero recuperar a última etapa de cada item com uma referência de lote.Então, se eu fizer um
Select * from HistoryTable where BatchRef = @BatchRef
Ele retornará todas as etapas para todos os itens do lote - por exemplo
Id Status BatchRef ItemCount 1 1 Batch001 100 1 2 Batch001 110 2 1 Batch001 60 2 2 Batch001 100
Mas o que eu realmente quero é:
Id Status BatchRef ItemCount 1 2 Batch001 110 2 2 Batch001 100
Editar:Desculpas - não consigo fazer com que as tags TABLE funcionem com Markdown - segui a ajuda ao pé da letra e parece bem na visualização
Solução
É meio difícil entender o design da sua tabela - acho que SO comeu seus delimitadores.
A maneira básica de lidar com isso é GROUP BY seus campos fixos e selecionar um MAX (ou MIN) para algum valor exclusivo (uma data e hora geralmente funciona bem).No seu caso, eu pensar que GROUP BY seria BatchRef e ItemCount, e Id seria sua coluna exclusiva.
Em seguida, junte-se novamente à tabela para obter todas as colunas.Algo como:
SELECT *
FROM HistoryTable
JOIN (
SELECT
MAX(Id) as Id.
BatchRef,
ItemCount
FROM HsitoryTable
WHERE
BacthRef = @batchRef
GROUP BY
BatchRef,
ItemCount
) as Latest ON
HistoryTable.Id = Latest.Id
Outras dicas
Supondo que você tenha uma coluna de identidade na tabela...
select
top 1 <fields>
from
HistoryTable
where
BatchRef = @BatchRef
order by
<IdentityColumn> DESC
Supondo que os IDs dos itens sejam numerados de forma incremental:
--Declare a temp table to hold the last step for each item id
DECLARE @LastStepForEach TABLE (
Id int,
Status int,
BatchRef char(10),
ItemCount int)
--Loop counter
DECLARE @count INT;
SET @count = 0;
--Loop through all of the items
WHILE (@count < (SELECT MAX(Id) FROM HistoryTable WHERE BatchRef = @BatchRef))
BEGIN
SET @count = @count + 1;
INSERT INTO @LastStepForEach (Id, Status, BatchRef, ItemCount)
SELECT Id, Status, BatchRef, ItemCount
FROM HistoryTable
WHERE BatchRef = @BatchRef
AND Id = @count
AND Status =
(
SELECT MAX(Status)
FROM HistoryTable
WHERE BatchRef = @BatchRef
AND Id = @count
)
END
SELECT *
FROM @LastStepForEach
SELECT id, status, BatchRef, MAX(itemcount) AS maxItemcount
FROM HistoryTable GROUP BY id, status, BatchRef
HAVING status > 1
É um pouco difícil decifrar seus dados da maneira como o WMD os formatou, mas você pode usar o tipo de truque necessário com expressões de tabela comuns no SQL 2005:
with LastBatches as (
select Batch, max(Id)
from HistoryTable
group by Batch
)
select *
from HistoryTable h
join LastBatches b on b.Batch = h.Batch and b.Id = h.Id
Ou uma subconsulta (supondo que o grupo na subconsulta funcione - de cara, não me lembro):
select *
from HistoryTable h
join (
select Batch, max(Id)
from HistoryTable
group by Batch
) b on b.Batch = h.Batch and b.Id = h.Id
Editar:Eu estava presumindo que você queria o último item para todo lote.Se você precisar apenas de um lote, as outras respostas (fazer o primeiro e ordenar em ordem decrescente) são o caminho a seguir.
Como já sugerido, você provavelmente deseja reordenar sua consulta para classificá-la na outra direção, para realmente buscar a primeira linha.Então você provavelmente gostaria de usar algo como
SELECT TOP 1 ...
se você estiver usando MSSQL 2k ou anterior, ou a variante compatível com SQL
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
columns
FROM tablename
) AS foo
WHERE rownumber = n
para qualquer outra versão (ou para outros sistemas de banco de dados que suportem a notação padrão), ou
SELECT ... LIMIT 1 OFFSET 0
para algumas outras variantes sem o suporte SQL padrão.
Veja também esse pergunta para alguma discussão adicional sobre a seleção de linhas.Usar a função agregada max() pode ou não ser mais rápido, dependendo se o cálculo do valor requer uma varredura da tabela.