Received: from maia.hub.org (unknown [200.46.204.183]) by mail.postgresql.org (Postfix) with ESMTP id AC2296325D5 for ; Thu, 4 Feb 2010 23:05:30 -0400 (AST) Received: from mail.postgresql.org ([200.46.204.86]) by maia.hub.org (mx1.hub.org [200.46.204.183]) (amavisd-maia, port 10024) with ESMTP id 88249-03 for ; Fri, 5 Feb 2010 03:05:18 +0000 (UTC) X-Greylist: from auto-whitelisted by SQLgrey-1.7.6 Received: from mail-pz0-f201.google.com (mail-pz0-f201.google.com [209.85.222.201]) by mail.postgresql.org (Postfix) with ESMTP id 7DB89633516 for ; Thu, 4 Feb 2010 23:04:43 -0400 (AST) Received: by pzk39 with SMTP id 39so3385254pzk.15 for ; Thu, 04 Feb 2010 19:04:41 -0800 (PST) MIME-Version: 1.0 Received: by 10.142.8.40 with SMTP id 40mr1288465wfh.247.1265339081556; Thu, 04 Feb 2010 19:04:41 -0800 (PST) In-Reply-To: References: <603c8f071002031905w51dbea37y148284ea6546feda@mail.gmail.com> <2f4958ff1002040028j29e907c6l47e1f5efe83b667d@mail.gmail.com> Date: Thu, 4 Feb 2010 22:04:41 -0500 Message-ID: Subject: Re: Slow query: table iteration (8.3) From: Glenn Maynard To: pgsql-performance@postgresql.org Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable X-Virus-Scanned: Maia Mailguard 1.0.1 X-Spam-Status: No, hits=-2.599 tagged_above=-10 required=5 tests=BAYES_00=-2.599 X-Spam-Level: X-Archive-Number: 201002/97 X-Sequence-Number: 37518 On Thu, Feb 4, 2010 at 4:09 AM, Glenn Maynard wrote: > But I'll be dropping this db into 8.4 soon to see if it helps > anything, and I'll check again (and if it's still slow I'll post more > details). =C2=A0It's been a while and I might just have been doing > something wrong. Windowing doesn't want to scale for this case. I'll simplify to give an actual test case. create table test_users (id serial primary key); insert into test_users (id) select generate_series(1, 1000); create table test (id serial primary key, score integer, user_id integer); insert into test (user_id, score) select s.id, random() * 1000000 from (select generate_series(1, 1000) as id) as s, generate_series(1, 1000); create index test_1 on test (score); create index test_2 on test (user_id, score desc); analyze; This generates a thousand users, with a thousand scores each. This finds the top score for each user (ignoring the detail of duplicate scores; easy to deal with): SELECT sub.id FROM ( SELECT t.id, rank() OVER (PARTITION BY t.user_id ORDER BY score DESC) AS rank FROM test t ) AS sub WHERE rank <=3D 1; This does use the test_2 index (as intended), but it's still very slow: 11 seconds on my system. It seems like it's doing a *complete* scan of the index, generating ranks for every row, and then filters out all but the first of each rank. That means it scales linearly with the total number of rows. All it really needs to do is jump to each user in the index and pull out the first entry (according to the "score desc" part of the test_2 index), which would make it scale linearly with the number of users. The function version: CREATE FUNCTION high_score_for_user(user_id int) RETURNS SETOF INTEGER LANGUAGE SQL AS $$ SELECT t.id FROM test t WHERE t.user_id =3D $1 ORDER BY t.score DESC LIMIT 1 $$; SELECT high_score_for_user(u.id) FROM test_users u; runs in 100ms. I think I'm stuck with either creating temporary functions with the constants already replaced, or creating an SQL function that evaluates a brand new query as a string as Yeb suggested. --=20 Glenn Maynard