Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dOXX7-0000dP-Be for pgsql-performance@arkaria.postgresql.org; Fri, 23 Jun 2017 22:58:33 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.84_2) (envelope-from ) id 1dOXX6-00069Q-RL for pgsql-performance@arkaria.postgresql.org; Fri, 23 Jun 2017 22:58:32 +0000 Received: from makus.postgresql.org ([2001:4800:1501:1::229]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1dOXX6-00069H-BP for pgsql-performance@postgresql.org; Fri, 23 Jun 2017 22:58:32 +0000 Received: from mail-ua0-x22a.google.com ([2607:f8b0:400c:c08::22a]) by makus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.84_2) (envelope-from ) id 1dOXX4-0004Pi-1T for pgsql-performance@postgresql.org; Fri, 23 Jun 2017 22:58:31 +0000 Received: by mail-ua0-x22a.google.com with SMTP id z22so44708245uah.1 for ; Fri, 23 Jun 2017 15:58:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:from:date:message-id:subject:to; bh=ZcjTTL8SK5jFNY02INuM8te2UvTIy2/G2FG+iWRZcP4=; b=mBPa/qt2jNlrgfWK2r7cwznBbOozWQo6JTt1AQQj+/jXUoXQGtMGy8qjl14k90yiS8 zZ9jF92OzZpE7pJKneEOjroQ2mAC5NZtKy6+3mW14cVZhEKkxyCP7BjTDewLHtmWj22A FZ1YVjcFzjShk7MNIteGD+vqtfOiynRTE8qudNGE0NAUfScIEQ4bExoFntuP8fdR04Gp YwZ+EaZJ3NnumJig92IMFsNUONzHdRFdTvZXHi1n6s+BVuPSKZwgCFAYzhaHMOMSumJI 2v/q1wMihrBz4tx78RFRQdPJdO9EA815S386+uQ/cnFtYvlvhgi9SFrtOat53OWRJDQQ H8kA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=ZcjTTL8SK5jFNY02INuM8te2UvTIy2/G2FG+iWRZcP4=; b=SJVPqVlJbCWyxchtG/LGg3NH4J468UjtHEYbAOtiwayGjoG7pStHlqk/OR6PdshQ5Z c9KVRLAy1JFrbEW3YwcuRtpyCMejq1azbbg6Zx2iUpzMiYCvbhoQCnC+2AB0F2TaBO/g xz79uLxa2fTc3Gm57pGDgpYVfOsfIQlrQ8/UwliiwjxZLlSAb4QRYKUWlWWFKnyedpxz a7OH1IG2i/jbjhWj6nF6wjhh18aPvdO3KxrosOMyZvdf0sF8MfL1da1O391we4FwyDs2 DYbOwBXd/03RA77tPKAmuFSHi5NmKar32lWtsBTPar+gq5qIMVDCaW4qkfsPaxpa2KYT sB1Q== X-Gm-Message-State: AKS2vOyllUl9zAUgFIUxd+Z/FUe0g4hLXLTAk55mTorcgWNFibJ70S2v 0hu4eEPd5yFzsGsQ6z162xMfEENMHmID X-Received: by 10.176.82.236 with SMTP id w41mr7442635uaw.98.1498258708992; Fri, 23 Jun 2017 15:58:28 -0700 (PDT) MIME-Version: 1.0 Received: by 10.176.76.230 with HTTP; Fri, 23 Jun 2017 15:58:28 -0700 (PDT) From: Clint Miller Date: Fri, 23 Jun 2017 17:58:28 -0500 Message-ID: Subject: Efficiently merging and sorting collections of sorted rows To: pgsql-performance@postgresql.org Content-Type: multipart/alternative; boundary="94eb2c191c262197e10552a88a6f" List-Archive: List-Help: List-ID: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-Mailing-List: pgsql-performance Precedence: bulk Sender: pgsql-performance-owner@postgresql.org --94eb2c191c262197e10552a88a6f Content-Type: text/plain; charset="UTF-8" Let's say I have the following table and index: create table foo(s text, i integer); create index foo_idx on foo (s, i); If I run the following commands: start transaction; set local enable_sort = off; explain analyze select * from foo where s = 'a' order by i; end; I get the following query plan: Index Only Scan using foo_idx on foo (cost=0.14..8. 15 row=1 width=36) (actual time=0.008..0.0 10 rows=3 loops=1) Index Cond: (s = 'a'::text) Heap Fetches: 3 That's a good plan because it's not doing a quick sort. Instead, it's just reading the sort order off of the index, which is exactly what I want. (I had to disable enable_sort because I didn't have enough rows of test data in the table to get Postgres to use the index. But if I had enough rows, the enable_sort stuff wouldn't be necessary. My real table has lots of rows and doesn't need enable_sort turned off to do the sort with the index.) But, if I run the following commands: start transaction; set local enable_sort = off; explain analyze select * from foo where s = 'a' or s = 'b' order by i; end; I get the following plan: Sort (cost=10000000001.16..10000000001.16 rows=2 width=36) (actual time=0.020..0.021 rows=7 loops=1) Sort Key: i Sort Method: quicksort Memory: 25kB -> Seq Scan on foo (cost=0.00..1.15 rows=2 width=36) (actual time=0.009..0.011 rows=7 loops=1) Filter: ((s = 'a'::text) OR (s = 'b'::text)) Rows Removed by Filter: 3 Here, it's loading the full result set into memory and doing a quick sort. (I think that's what it's doing, at least. If that's not the case, let me know.) That's not good. What I'd really like Postgres to do is use the index to get a sorted list of rows where s = 'a'. Then, use the index again to get a sorted list of rows where s = 'b'. Then it seems like Postgres should be able to merge the sorted lists into a single sorted result set in O(n) time and O(1) memory using a single merge operation. Am I doing something wrong here? Is there a way to get Postgres to not do a quick sort here? My concern is that my real table has a significant number of rows, and the result set will not fit into memory. So instead of getting a quick sort, I'll end up getting a slow, disk-based merge sort. I really need the bulk of the sort operation to come off of the index so that time and memory are small. Thanks for any help on this issue. --94eb2c191c262197e10552a88a6f Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Let's say I have the following table and index:
create table foo(s text, i integer);
create ind= ex foo_idx on foo (s, i);

If I run t= he following commands:

start transaction;
set local enable_sort =3D off;
explain analyze select * fr= om foo where s =3D 'a' order by i;
end;
<= br>
I get the following query plan:

<= div>
<= div>Index Only Scan using foo_idx on foo (cost=3D0.14..8. 15 row=3D1 width= =3D36) (actual time=3D0.008..0.0 10 rows=3D3 loops=3D1)
<= blockquote style=3D"margin:0px 0px 0px 40px;border:none;padding:0px">
= =C2=A0 Index Cond: (s =3D 'a'::text)
=C2=A0 Heap = Fetches: 3

That's a good pl= an because it's not doing a quick sort. Instead, it's just reading = the sort order off of the index, which is exactly what I want. (I had to di= sable enable_sort because I didn't have enough rows of test data in the= table to get Postgres to use the index. But if I had enough rows, the enab= le_sort stuff wouldn't be necessary. My real table has lots of rows and= doesn't need enable_sort turned off to do the sort with the index.)

But, if I run the following commands:

=
start transaction;
set local enable_sort =3D off;=
explain analyze select * from foo where s =3D 'a' or s = =3D 'b' order by i;
end;

I get the following plan:

Sort =C2=A0(= cost=3D10000000001.16..10000000001.16 rows=3D2 width=3D36) (actual time=3D0= .020..0.021 rows=3D7 loops=3D1)
=C2=A0 Sort Key: i
=C2= =A0 Sort Method: quicksort =C2=A0Memory: 25kB
=C2=A0 -> =C2=A0= Seq Scan on foo =C2=A0(cost=3D0.00..1.15 rows=3D2 width=3D36) (actual time= =3D0.009..0.011 rows=3D7 loops=3D1)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 F= ilter: ((s =3D 'a'::text) OR (s =3D 'b'::text))
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 Rows Removed by Filter: 3

<= /div>
Here, it's loading the full result set into memory an= d doing a quick sort. (I think that's what it's doing, at least. If= that's not the case, let me know.) That's not good.

=
What I'd really like Postgres to do is use the index to get = a sorted list of rows where s =3D 'a'. Then, use the index again to= get a sorted list of rows where s =3D 'b'. Then it seems like Post= gres should be able to merge the sorted lists into a single sorted result s= et in O(n) time and O(1) memory using a single merge operation.
<= br>
Am I doing something wrong here? Is there a way to get Postgr= es to not do a quick sort here?

My concern is that= my real table has a significant number of rows, and the result set will no= t fit into memory. So instead of getting a quick sort, I'll end up gett= ing a slow, disk-based merge sort. I really need the bulk of the sort opera= tion to come off of the index so that time and memory are small.
=
Thanks for any help on this issue.
--94eb2c191c262197e10552a88a6f--