Récupérer le dernier élément d'une table - SQL
-
09-06-2019 - |
Question
J'ai une table d'historique dans SQL Server qui suit essentiellement un élément tout au long d'un processus.L'élément comporte des champs fixes qui ne changent pas tout au long du processus, mais comporte quelques autres champs, notamment le statut et l'identifiant, qui s'incrémentent à mesure que les étapes du processus augmentent.
Fondamentalement, je souhaite récupérer la dernière étape pour chaque élément étant donné une référence de lot.Donc si je fais un
Select * from HistoryTable where BatchRef = @BatchRef
Il renverra toutes les étapes pour tous les éléments du lot - par exemple
Identifiant Statut BatchRef ItemCount 1 1 Batch001 100 1 2 Batch001 110 2 1 Batch001 60 2 2 Batch001 100
Mais ce que je veux vraiment c'est :
Identifiant Statut BatchRef ItemCount 1 2 Batch001 110 2 2 Batch001 100
Modifier:Toutes mes excuses - je n'arrive pas à faire fonctionner les balises TABLE avec Markdown - j'ai suivi l'aide à la lettre et cela semble bien dans l'aperçu
La solution
Il est un peu difficile de donner un sens à la conception de votre table – je pense que vous avez tellement mangé vos délimiteurs.
La manière de base de gérer cela consiste à GROUPER PAR vos champs fixes et à sélectionner un MAX (ou MIN) pour une valeur unique (une date/heure fonctionne généralement bien).Dans votre cas, je pense que GROUP BY serait BatchRef et ItemCount, et Id sera votre colonne unique.
Ensuite, revenez à la table pour obtenir toutes les colonnes.Quelque chose comme:
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
Autres conseils
En supposant que vous ayez une colonne d'identité dans le tableau...
select
top 1 <fields>
from
HistoryTable
where
BatchRef = @BatchRef
order by
<IdentityColumn> DESC
En supposant que les identifiants d'articles sont numérotés de manière incrémentielle :
--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
Il est un peu difficile de déchiffrer vos données telles que WMD les a formatées, mais vous pouvez utiliser le type d'astuce dont vous avez besoin avec les expressions de table courantes sur 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 une sous-requête (en supposant que le groupe by dans la sous-requête fonctionne - de mémoire, je ne me souviens pas) :
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
Modifier:Je pensais que tu voulais le dernier objet pour chaque lot.Si vous en avez juste besoin pour un lot, les autres réponses (faire un top 1 et classer par ordre décroissant) sont la voie à suivre.
Comme déjà suggéré, vous souhaiterez probablement réorganiser votre requête pour la trier dans l'autre sens afin de récupérer la première ligne.Ensuite, vous voudriez probablement utiliser quelque chose comme
SELECT TOP 1 ...
si vous utilisez MSSQL 2k ou version antérieure, ou la variante compatible SQL
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
columns
FROM tablename
) AS foo
WHERE rownumber = n
pour toute autre version (ou pour d'autres systèmes de bases de données prenant en charge la notation standard), ou
SELECT ... LIMIT 1 OFFSET 0
pour certaines autres variantes sans le support SQL standard.
Voir également ce question pour une discussion supplémentaire sur la sélection des lignes.L'utilisation de la fonction d'agrégation max() peut être plus rapide ou non selon que le calcul de la valeur nécessite ou non une analyse de table.