Mam stół bez identyfikatorów. ma 3 kolumny: nazwę komputera, jego status (wł. / wył.) w momencie odpytywania oraz znacznik czasu wstawienia.
jeśli biegnę
select * from computers group by name;
Dostaję linię dla każdego komputera (jest ich 200), ale te linie nie zawsze zawierają najnowszy wpis. Próbowałem
select computers group by name order by timestamp asc;
Ale otrzymuję niespójne odpowiedzi (niektóre ostatnie znaczniki czasu, niektóre stare ... nie wiem dlaczego).
Jest to zasadniczo ten sam problem, co tutaj: SQL: GROUP BY rekordy, a następnie uzyskaj ostatni rekord z każdej grupy?, ale nie mam id do pomocy :(
Odpowiedzi:
1 dla odpowiedzi № 1Możesz pisać:
SELECT computers.name,
computers.status,
computers.timestamp
FROM ( SELECT name,
MAX(timestamp) AS max_timestamp
FROM computers
GROUP
BY name
) AS t
JOIN computers
ON computers.name = t.name
AND computers.timestamp = t.max_timestamp
;
Powyższe używa tego podzapytania do znalezienia największego timestamp
dla każdego name
:
SELECT name
MAX(timestamp) AS max_timestamp
FROM computers
GROUP
BY name
;
a następnie zbiera pola z computers
którego name
i timestamp
dopasuj coś, co zwróciło podzapytanie.
Powód, dla którego order by
klauzula nie ma żadnego skutku, ponieważ przychodzi zbyt „późno”: jest używana do zamawiania rekordów, które będą zwracane, po tym, jak już ustalono, że będzie Być zwracane. Cytować z § 11.16.3 ”GROUP BY
i HAVING
z ukrytymi kolumnami ”w MySQL 5.6 Reference Manual w tym temacie:
Serwer może wybrać dowolną wartość z każdegogrupa, więc jeśli nie są takie same, wybrane wartości są nieokreślone. Ponadto na wybór wartości z każdej grupy nie można wpływać przez dodanie
ORDER BY
klauzula. Sortowanie zestawu wyników następuje po wybraniu wartości iORDER BY
nie wpływa na wartości wybrane przez serwer.
Innym sposobem jest napisanie współzależny podzapytanie i zrezygnuj z GROUP BY
całkowicie. To:
SELECT name, status, timestamp
FROM computers AS c1
WHERE NOT EXISTS
( SELECT 1
FROM computers
WHERE name = c1.name
AND timestamp > c1.timestamp
)
;
znajduje wszystkie wiersze w computers
to nie zostało zastąpione przez nowsze wiersze tego samego name
. To samo podejście można wykonać przy połączeniu:
SELECT c1.name, c1.status, c1.timestamp
FROM computers AS c1
LEFT
OUTER
JOIN computers AS c2
ON c2.name = c1.name
AND c2.timestamp > c1.timestamp
WHERE c2.name IS NULL
;
który jest mniej wyraźny IMHO, ale może działać lepiej.