Oto pięć opcji użycia SQL do zwrócenia tylko tych wierszy, które mają minimalną wartość w swojej grupie.
Te przykłady działają w większości głównych RDBMS, w tym MySQL, MariaDB, Oracle, PostgreSQL, SQLite i SQL Server.
Przykładowe dane
Załóżmy, że mamy tabelę z następującymi danymi:
SELECT * FROM Gameshow;
Wynik:
+--------------+--------+---------+ | Contestant | Game | Score | |--------------+--------+---------| | Faye | 1 | 85 | | Faye | 2 | 50 | | Faye | 3 | 63 | | Jet | 1 | 31 | | Jet | 2 | 40 | | Jet | 3 | 51 | | Spike | 1 | 25 | | Spike | 2 | 27 | | Spike | 3 | 15 | +--------------+--------+---------+
I załóżmy, że chcemy uzyskać najniższy wynik dla każdego uczestnika.
Opcja 1
Szybkim i łatwym rozwiązaniem jest skonstruowanie zapytania za pomocą SQL GROUP BY
klauzula:
SELECT
Contestant,
MIN( Score ) AS MinScore
FROM Gameshow
GROUP BY Contestant
ORDER BY Contestant;
Wynik:
+--------------+------------+ | Contestant | MinScore | |--------------+------------| | Faye | 50 | | Jet | 31 | | Spike | 15 | +--------------+------------+
Opcja 2
Jeśli chcemy uwzględnić grę, w którą grał każdy zawodnik, aby uzyskać minimalny wynik, jednym ze sposobów na to jest użycie skorelowanego podzapytania w ten sposób:
SELECT
Contestant,
Game,
Score
FROM Gameshow g1
WHERE Score = ( SELECT MIN( g2.Score )
FROM Gameshow g2
WHERE g1.Contestant = g2.Contestant )
ORDER BY Contestant;
Wynik:
+--------------+--------+---------+ | Contestant | Game | Score | |--------------+--------+---------| | Faye | 2 | 50 | | Jet | 1 | 31 | | Spike | 3 | 15 | +--------------+--------+---------+
Skorelowane podzapytania odwołują się do co najmniej jednej kolumny spoza podzapytania. Skorelowane podzapytania mogą być nieefektywne, głównie ze względu na fakt, że podzapytanie jest wykonywane wielokrotnie, raz dla każdego wiersza, który może zostać wybrany przez zapytanie zewnętrzne. Skorelowane podzapytania są również nazywane podzapytaniami powtarzającymi się.
Opcja 3
Alternatywnie możemy użyć nieskorelowanego podzapytania w ten sposób:
SELECT
g1.Contestant,
g1.Game,
g1.Score
FROM Gameshow g1
JOIN (
SELECT Contestant, MIN( Score ) AS Score
FROM Gameshow
GROUP BY Contestant ) AS g2
ON g1.Contestant = g2.Contestant AND g1.Score = g2.Score
ORDER BY Contestant ASC;
Wynik:
+--------------+--------+---------+ | Contestant | Game | Score | |--------------+--------+---------| | Faye | 2 | 50 | | Jet | 1 | 31 | | Spike | 3 | 15 | +--------------+--------+---------+
Nieskorelowane podzapytania nie zależą od zewnętrznego zapytania do ich wykonania. Mogą działać całkowicie niezależnie od zewnętrznego zapytania.
W Oracle musimy usunąć AS
podczas deklarowania aliasów kolumn:
SELECT
g1.Contestant,
g1.Game,
g1.Score
FROM Gameshow g1
JOIN (
SELECT Contestant, MIN( Score ) Score
FROM Gameshow
GROUP BY Contestant ) g2
ON g1.Contestant = g2.Contestant AND g1.Score = g2.Score
ORDER BY Contestant ASC;
Opcja 4
Innym sposobem na pobranie wierszy z minimalną wartością w danej kolumnie jest użycie LEFT JOIN
, tak:
SELECT
g1.Contestant,
g1.Game,
g1.Score
FROM Gameshow g1
LEFT JOIN Gameshow g2 ON
g1.Contestant = g2.Contestant AND g1.Score > g2.Score
WHERE g2.Contestant IS NULL
ORDER BY g1.Contestant ASC;
Wynik:
+--------------+--------+---------+ | Contestant | Game | Score | |--------------+--------+---------| | Faye | 2 | 50 | | Jet | 1 | 31 | | Spike | 3 | 15 | +--------------+--------+---------+
Opcja 5
Innym sposobem na to jest użycie wspólnego wyrażenia tabelowego z funkcją okna:
WITH cte AS (
SELECT Contestant, Game, Score,
RANK() OVER ( PARTITION BY Contestant
ORDER BY Score ASC
) AS r
FROM Gameshow
)
SELECT Contestant, Game, Score
FROM cte
WHERE r = 1
ORDER BY Contestant ASC;
Wynik:
+--------------+--------+---------+ | Contestant | Game | Score | |--------------+--------+---------| | Faye | 2 | 50 | | Jet | 1 | 31 | | Spike | 3 | 15 | +--------------+--------+---------+