Letztes Element in einer Tabelle abrufen – SQL
-
09-06-2019 - |
Frage
Ich habe eine Verlaufstabelle in SQL Server, die im Grunde ein Element durch einen Prozess verfolgt.Das Element verfügt über einige feste Felder, die sich im Laufe des Prozesses nicht ändern, verfügt jedoch über einige andere Felder, einschließlich Status und ID, die mit der Anzahl der Schritte des Prozesses inkrementiert werden.
Grundsätzlich möchte ich den letzten Schritt für jedes Element mit einer Batch-Referenz abrufen.Also, wenn ich ein mache
Select * from HistoryTable where BatchRef = @BatchRef
Es werden alle Schritte für alle Artikel im Stapel zurückgegeben, z. B
Id Status BatchRef ItemCount 1 1 Batch001 100 1 2 Batch001 110 2 1 Batch001 60 2 2 Batch001 100
Aber was ich wirklich will, ist:
Id Status BatchRef ItemCount 1 2 Batch001 110 2 2 Batch001 100
Bearbeiten:Entschuldigung – ich kann die TABLE-Tags anscheinend nicht mit Markdown zum Laufen bringen – habe die Hilfe genau befolgt und sieht in der Vorschau gut aus
Lösung
Es ist ziemlich schwierig, das Design Ihrer Tabelle zu verstehen – ich denke, Ihre Trennzeichen sind SO übertrieben.
Die grundlegende Möglichkeit, dies zu handhaben, besteht darin, Ihre festen Felder nach GROUP BY zu gruppieren und einen MAX (oder MIN) für einen eindeutigen Wert auszuwählen (ein Datum/Uhrzeit-Wert funktioniert normalerweise gut).In Ihrem Fall ich denken dass das GROUP BY BatchRef und ItemCount wäre und Id Ihre eindeutige Spalte sein würde.
Verbinden Sie sich dann wieder mit der Tabelle, um alle Spalten zu erhalten.Etwas wie:
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
Andere Tipps
Angenommen, Sie haben eine Identitätsspalte in der Tabelle ...
select
top 1 <fields>
from
HistoryTable
where
BatchRef = @BatchRef
order by
<IdentityColumn> DESC
Angenommen, die Artikel-IDs sind fortlaufend nummeriert:
--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 ist etwas schwierig, Ihre Daten so zu entschlüsseln, wie WMD sie formatiert hat, aber Sie können mit gängigen Tabellenausdrücken in SQL 2005 den Trick anwenden, den Sie benötigen:
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
Oder eine Unterabfrage (vorausgesetzt, die Gruppierung nach in der Unterabfrage funktioniert – spontan weiß ich nicht mehr):
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
Bearbeiten:Ich ging davon aus, dass Sie den letzten Artikel wollten jeden Charge.Wenn Sie es nur für eine Charge benötigen, sind die anderen Antworten (Top 1 erstellen und absteigend ordnen) die richtige Wahl.
Wie bereits vorgeschlagen, möchten Sie Ihre Abfrage wahrscheinlich neu anordnen, um sie in die andere Richtung zu sortieren, sodass Sie tatsächlich die erste Zeile abrufen.Dann möchten Sie wahrscheinlich so etwas verwenden
SELECT TOP 1 ...
wenn Sie MSSQL 2k oder früher oder die SQL-kompatible Variante verwenden
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
columns
FROM tablename
) AS foo
WHERE rownumber = n
für jede andere Version (oder für andere Datenbanksysteme, die die Standardnotation unterstützen), oder
SELECT ... LIMIT 1 OFFSET 0
für einige andere Varianten ohne die Standard-SQL-Unterstützung.
Siehe auch Das Frage für eine zusätzliche Diskussion zum Auswählen von Zeilen.Die Verwendung der Aggregatfunktion max() kann schneller sein oder auch nicht, je nachdem, ob für die Berechnung des Werts ein Tabellenscan erforderlich ist.