Thread: Maxima per row

Maxima per row

From
"Moray McConnachie"
Date:
I have a table, one of whose fields indicates the date from which
information in the row becomes valid, and another indicates the type to
which the row refers. How do I design a query such that I get one row for
each type, that row being the most recent info about that type? I.e. what I
want is

CREATE TABLE info (id serial primary key,typeid int4,validfrom
date,description text);
INSERT INTO info (typeid,validfrom,description) VALUES (1,'01/01/1999','a');
INSERT INTO info (typeid,validfrom,description) VALUES (1,'01/01/2000','b');
INSERT INTO info (typeid,validfrom,description) VALUES (2,'01/01/1998','c');
INSERT INTO info (typeid,validfrom,description) VALUES (2,'01/01/1999','d');
INSERT INTO info (typeid,validfrom,description) VALUES (2,'01/01/1997','e');

SELECT typeid,validfrom FROM prices GROUP BY typeid HAVING
validfrom=MAX(validfrom);

but of course the last is illegal. The result I'm looking for is

1,'01/01/2000','b',
2,'01/01/1999','d'

Thanks,
Moray

----------------------------------------------------------------
Moray.McConnachie@computing-services.oxford.ac.uk



Re: Maxima per row

From
Tom Lane
Date:
"Moray McConnachie" <moray.mcconnachie@computing-services.oxford.ac.uk> writes:
> I have a table, one of whose fields indicates the date from which
> information in the row becomes valid, and another indicates the type to
> which the row refers. How do I design a query such that I get one row for
> each type, that row being the most recent info about that type?

Of course, if you *only* want to get the typeid and date, it's easy:

SELECT typeid, max(startdate) FROM table GROUP BY typeid;

The tricky part is getting back the rest of the row that contains the
maximum startdate value.  This approach can't do that.

You can do it with a subselect:

SELECT typeid, startdate, ... FROM table outer WHERE startdate =(SELECT max(startdate) FROM table inner WHERE
inner.typeid= outer.typeid);
 

or perhaps faster

SELECT typeid, startdate, ... FROM table outer WHERE NOT EXISTS(SELECT 1 FROM table inner WHERE inner.typeid =
outer.typeidAND       inner.startdate > outer.startdate);
 

but both of these are likely to be pretty slow, and they're not that
easy to understand either.  If you don't mind using non-SQL-standard
features, another way is with DISTINCT ON:

SELECT DISTINCT ON (typeid) typeid, startdate, ... FROM tableORDER BY typeid, startdate DESC;

(In 6.5, omit parentheses around DISTINCT ON argument.)  This orders
the data in the specified way and then drops all but the first row
of each group with the same typeid.  Since each such group is ordered
by startdate, you have your result.
        regards, tom lane