Received: from localhost (unknown [200.46.204.183]) by mail.postgresql.org (Postfix) with ESMTP id BDDCF633F9E for ; Tue, 7 Apr 2009 23:34:41 -0300 (ADT) Received: from mail.postgresql.org ([200.46.204.86]) by localhost (mx1.hub.org [200.46.204.183]) (amavisd-maia, port 10024) with ESMTP id 93047-03-2 for ; Tue, 7 Apr 2009 23:34:39 -0300 (ADT) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from yw-out-2324.google.com (yw-out-2324.google.com [74.125.46.31]) by mail.postgresql.org (Postfix) with ESMTP id EC8C2633937 for ; Tue, 7 Apr 2009 23:34:39 -0300 (ADT) Received: by yw-out-2324.google.com with SMTP id 3so1982332ywj.73 for ; Tue, 07 Apr 2009 19:34:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:date:message-id:subject :from:to:content-type:content-transfer-encoding; bh=TdQk67Emya+exmDi8tnZkzzan9E3TgzfXRlS55e8Om0=; b=saEViGcf+9IaeeTPM95lrYaLaCNL+oCs1M6zAsvrPhfzZ0SqrK7Lsj+w/MeJERuNbX 6ySa8OKlP/QLsQhFa7lmo56kKLmc2NSSwFERif/kkUMb4vP8ja+4G9AhJ/8jRFzAwKNc mJgMTGrA7Mcz0Sjbqe//TRImjSCPO5Eq7WpfA= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:date:message-id:subject:from:to:content-type :content-transfer-encoding; b=Ujhp0O5MXsO8xV2FtzNDcEUcKNw24yuzdWCTIPAhMv5De3c6QR2xJ4q3Ktuy1qSxk6 DNAz4RWfSwaBSD39WeefWirT5KSoxNcKcVK+kp9z+Gg2tJo9GMO1ACEHZZOVFSLOAQG0 ywa2vcT7bBCND1fhTg9jd3w8mY1g6GNyKPvwk= MIME-Version: 1.0 Received: by 10.151.13.9 with SMTP id q9mr37185ybi.239.1239158078435; Tue, 07 Apr 2009 19:34:38 -0700 (PDT) Date: Tue, 7 Apr 2009 22:34:38 -0400 Message-ID: Subject: Nested selects From: Glenn Maynard To: pgsql-sql@postgresql.org Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Virus-Scanned: Maia Mailguard 1.0.1 X-Spam-Status: No, hits=0.315 tagged_above=0 required=5 tests=SARE_MILLIONSOF=0.315 X-Spam-Level: X-Archive-Number: 200904/42 X-Sequence-Number: 32388 I'm deriving high scores from two tables: one containing data for each time a user played (rounds), and one containing a list of stages: CREATE TABLE stage (id SERIAL NOT NULL PRIMARY KEY, name VARCHAR); CREATE TABLE round (id SERIAL NOT NULL PRIMARY KEY, score REAL, stage_id INTEGER REFERENCES stage (id)); INSERT INTO stage (name) VALUES ('stage 1'), ('stage 2'), ('stage 3'); INSERT INTO round (stage_id, score) VALUES (1, 100), (1, 150), (1, 175), (2, 250), (2, 275), (2, 220), (3, 350), (3, 380), (3, 322); SELECT r.* FROM round r WHERE r.id IN ( -- Get the high scoring round ID for each stage: SELECT ( -- Get the high score for stage s: SELECT r.id FROM round r WHERE r.stage_id = s.id ORDER BY r.score DESC LIMIT 1 ) FROM stage s ); This works fine, and with a (stage_id, score DESC) index, is reasonably fast with around 1000 stages. round may expand to millions of rows. Unfortunately, it doesn't generalize to getting the top N scores for each stage; LIMIT 2 isn't valid ("more than one row returned by a subquery used as an expression"). I fiddled with putting the inner results in an array, without much luck, and I'm not sure how well that'd optimize. Having the results in any particular order isn't important. (In practice, the inner select will often be more specific--"high scores on the west coast", "high scores this month", and so on.) This seems embarrassingly simple: return the top rounds for each stage--but I'm banging my head on it for some reason. -- Glenn Maynard