Minulla on tämä taulukko asiakirjoja varten (yksinkertaistettu versio tässä):
+------+-------+--------------------------------------+
| id | rev | content |
+------+-------+--------------------------------------+
| 1 | 1 | ... |
| 2 | 1 | ... |
| 1 | 2 | ... |
| 1 | 3 | ... |
+------+-------+--------------------------------------+
Miten valitsen yhden rivin per id ja vain suurimman rev?
Yllä olevilla tiedoilla tuloksen pitäisi sisältää kaksi riviä: [1, 3, ...]
ja [2, 1, ...]
. Käytän MySQL:ää.
Tällä hetkellä käytän tarkastuksia while
-silmukassa havaitakseni ja korvatakseni vanhat kierrokset tulosjoukosta. Mutta onko tämä ainoa tapa saavuttaa tulos? Eikö ole olemassa SQL-ratkaisua?
Päivitys Kuten vastauksista käy ilmi, SQL-ratkaisu on olemassa, ja tässä sqlfiddle-demo.
Päivitys 2 Huomasin edellä mainitun sqlfiddle:n lisäämisen jälkeen, että kysymystä on äänestetty enemmän kuin vastauksia. Tämä ei ole ollut tarkoitus! Fiddle perustuu vastauksiin, erityisesti hyväksyttyyn vastaukseen.
Tarvitset vain GROUP BY
-lausekkeen ja MAX
-aggregaattifunktion:
SELECT id, MAX(rev)
FROM YourTable
GROUP BY id
Huomasin juuri, että tarvitset myös sarakkeen "sisältö".
Tämä on hyvin yleinen kysymys SQL:ssä: etsi koko data riville, jolla on jokin maksimiarvo sarakkeessa jonkin ryhmän tunnisteen mukaan. Olen kuullut tuon usein urani aikana. Itse asiassa se oli yksi niistä kysymyksistä, joihin vastasin nykyisessä työpaikassani teknisessä haastattelussa.
Se on itse asiassa niin yleistä, että StackOverflow-yhteisö on luonut yhden tunnisteen juuri tällaisia kysymyksiä varten: [tag:greatest-n-per-group].
Ongelman ratkaisemiseen on periaatteessa kaksi lähestymistapaa:
group-identifier, max-value-in-group
alakyselyllä.Tässä lähestymistavassa etsitään ensin group-identifier, max-value-in-group
(ratkaistu jo edellä) alakyselyssä. Sitten liitetään taulukko alakyselyyn siten, että sekä group-identifier
että max-value-in-group
ovat yhtä suuret:
SELECT a.id, a.rev, a.contents
FROM YourTable a
INNER JOIN (
SELECT id, MAX(rev) rev
FROM YourTable
GROUP BY id
) b ON a.id = b.id AND a.rev = b.rev
Tässä lähestymistavassa taulukko liitetään vasemmalla liitoksella itsensä kanssa. Yhtäläisyys on group-identifier
. Sitten 2 fiksua siirtoa:
NULL
rivillä (riveillä), joilla todella on suurin arvo (se on LEFT JOIN
, muistatko?). Sitten suodatamme yhdistetyn tuloksen ja näytämme vain ne rivit, joiden oikealla puolella on NULL
.Tulokseksi saadaan siis:
SELECT a.*
FROM YourTable a
LEFT OUTER JOIN YourTable b
ON a.id = b.id AND a.rev < b.rev
WHERE b.id IS NULL;
Molemmat lähestymistavat johtavat täsmälleen samaan tulokseen.
Jos sinulla on kaksi riviä, joilla on group-identifier
-arvona max-value-in-group
, molemmat rivit ovat tuloksessa molemmilla lähestymistavoilla.
Molemmat lähestymistavat ovat SQL ANSI -yhteensopivia, joten ne toimivat suosikki RDBMS-tietokannan kanssa sen "maku" -ominaisuuksista riippumatta.
Molemmat lähestymistavat ovat myös suorituskykyystävällisiä, mutta käyttötapa voi vaihdella (RDBMS, tietokannan rakenne, indeksit jne.). Kun siis valitset yhden lähestymistavan toisen sijaan, vertaile. Ja varmista, että valitset sen, joka on sinusta järkevin.
Haluan käyttää mahdollisimman vähän koodia...
Voit tehdä sen käyttämällä IN
kokeile tätä:
SELECT *
FROM t1 WHERE (id,rev) IN
( SELECT id, MAX(rev)
FROM t1
GROUP BY id
)
mielestäni se on vähemmän monimutkainen... helpompi lukea ja ylläpitää.
Jotain tällaista?
SELECT yourtable.id, rev, content
FROM yourtable
INNER JOIN (
SELECT id, max(rev) as maxrev FROM yourtable
WHERE yourtable
GROUP BY id
) AS child ON (yourtable.id = child.id) AND (yourtable.rev = maxrev)