Obtener el último elemento de una tabla - SQL
-
09-06-2019 - |
Pregunta
Tengo una tabla de historial en SQL Server que básicamente rastrea un elemento a través de un proceso.El elemento tiene algunos campos fijos que no cambian durante el proceso, pero tiene algunos otros campos, incluidos el estado y la identificación, que aumentan a medida que aumentan los pasos del proceso.
Básicamente, quiero recuperar el último paso para cada elemento con una referencia de lote.Entonces si hago un
Select * from HistoryTable where BatchRef = @BatchRef
Devolverá todos los pasos para todos los artículos del lote, por ejemplo
ID Estado Referencia de lote Número de elementos 1 1 Batch001 100 1 2 Batch001 110 2 1 Batch001 60 2 2 Batch001 100
Pero lo que realmente quiero es:
ID Estado Referencia de lote Número de elementos 1 2 Batch001 110 2 2 Batch001 100
Editar:Disculpas: parece que no puedo hacer que las etiquetas TABLE funcionen con Markdown; seguí la ayuda al pie de la letra y se ve bien en la vista previa.
Solución
Es un poco difícil entender el diseño de su tabla; creo que SO se comió sus delimitadores.
La forma básica de manejar esto es AGRUPAR POR sus campos fijos y seleccionar un MÁXIMO (o MIN) para algún valor único (una fecha y hora generalmente funciona bien).En tu caso yo pensar que GROUP BY sería BatchRef y ItemCount, y Id será su columna única.
Luego, vuelva a unirse a la tabla para obtener todas las columnas.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
Otros consejos
Suponiendo que tiene una columna de identidad en la tabla...
select
top 1 <fields>
from
HistoryTable
where
BatchRef = @BatchRef
order by
<IdentityColumn> DESC
Suponiendo que los ID de los artículos estén 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
Es un poco difícil descifrar sus datos de la forma en que WMD los ha formateado, pero puede realizar el tipo de truco que necesita con expresiones de tabla comunes en 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
O una subconsulta (suponiendo que el grupo by en la subconsulta funcione, no lo recuerdo):
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:Estaba asumiendo que querías el último artículo para cada lote.Si solo lo necesita para un lote, entonces las otras respuestas (hacer un top 1 y ordenar descendente) son el camino a seguir.
Como ya se sugirió, probablemente desee reordenar su consulta para ordenarla en la otra dirección para obtener la primera fila.Entonces probablemente quieras usar algo como
SELECT TOP 1 ...
si está utilizando MSSQL 2k o anterior, o la variante compatible con SQL
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
columns
FROM tablename
) AS foo
WHERE rownumber = n
para cualquier otra versión (o para otros sistemas de bases de datos que admitan la notación estándar), o
SELECT ... LIMIT 1 OFFSET 0
para algunas otras variantes sin el soporte SQL estándar.
Ver también este pregunta para una discusión adicional sobre la selección de filas.El uso de la función agregada max() puede ser más rápido o no dependiendo de si el cálculo del valor requiere un escaneo de la tabla.