[PERFORM] Re: Fwd: Slow query from ~7M rows, joined to two tables of ~100 rows each

2017-06-23 Thread Karl Czajkowski
On Jun 23, Chris Wilson modulated:
> ...
> create table metric_pos (id serial primary key, pos integer);
> create index idx_metric_pos_id_pos on metric_pos (id, pos);
> ...
> create table asset_pos (id serial primary key, pos integer);
> ...

Did you only omit a CREATE INDEX statement on asset_pos (id, pos) from
your problem statement or also from your actual tests?  Without any
index, you are forcing the query planner to do that join the hard way.


> CREATE TABLE metric_value
> (
>   id_asset integer NOT NULL,
>   id_metric integer NOT NULL,
>   value double precision NOT NULL,
>   date date NOT NULL,
>   timerange_transaction tstzrange NOT NULL,
>   id bigserial NOT NULL,
>   CONSTRAINT cons_metric_value_pk PRIMARY KEY (id)
> )
> WITH (
>   OIDS=FALSE
> );
>
> ...
> CREATE INDEX idx_metric_value_id_metric_id_asset_date ON
> metric_value (id_metric, id_asset, date, timerange_transaction,
> value);
> ...

Have you tried adding a foreign key constraint on the id_asset and
id_metric columns?  I wonder if you'd get a better query plan if the
DB knew that the inner join would not change the number of result
rows.  I think it's doing the join inside the filter step because
it assumes that the inner join may drop rows.

Also, did you include an ANALYZE step between your table creation
statements and your query benchmarks?  Since you are dropping and
recreating test data, you have no stats on anything.


> This is an example of the kind of query we would like to speed up:
>
>
> SELECT metric_pos.pos AS pos_metric, asset_pos.pos AS pos_asset,
> date, value
> FROM metric_value
> INNER JOIN asset_pos ON asset_pos.id = metric_value.id_asset
> INNER JOIN metric_pos ON metric_pos.id = metric_value.id_metric
> WHERE
> date >= '2016-01-01' and date < '2016-06-01'
> AND timerange_transaction @> current_timestamp
> ORDER BY metric_value.id_metric, metric_value.id_asset, date
>

How sparse is the typical result set selected by these date and
timerange predicates?  If it is sparse, I'd think you want your
compound index to start with those two columns.

Finally, your subject line said you were joining hundreds of rows to
millions.  In queries where we used a similarly small dimension table
in the WHERE clause, we saw massive speedup by pre-evaluating that
dimension query to produce an array of keys, the in-lining the actual
key constants in the where clause of a main fact table query that
no longer had the join in it.

In your case, the equivalent hack would be to compile the small
dimension tables into big CASE statements I suppose...


Karl


-- 
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance


Re: [PERFORM] Efficiently merging and sorting collections of sorted rows

2017-06-23 Thread Tom Lane
Clint Miller  writes:
> 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.)

TBH, I think this whole argument is proceeding from false premises.
Using an indexscan as a substitute for an explicit sort of lots of
rows isn't all that attractive, because it implies a whole lot of
random access to the table (unless the table is nearly in index
order, which isn't a condition you can count on without expending
a lot of maintenance effort to keep it that way).  seqscan-and-sort
is often a superior alternative, especially if you're willing to give
the sort a reasonable amount of work_mem.

> 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.

If there's no duplicates to remove, I think this will work:

explain
(select * from foo a where s = 'a' order by i)
union all
(select * from foo b where s = 'b' order by i)
order by i;

 Merge Append  (cost=0.32..48.73 rows=12 width=36)
   Sort Key: a.i
   ->  Index Only Scan using foo_idx on foo a  (cost=0.15..24.26 rows=6 
width=36)
 Index Cond: (s = 'a'::text)
   ->  Index Only Scan using foo_idx on foo b  (cost=0.15..24.26 rows=6 
width=36)
 Index Cond: (s = 'b'::text)

In this case it's pretty obvious that the two union arms can never
return the same row, but optimizing OR into UNION in general is
difficult because of the possibility of duplicates.  I wouldn't
recommend holding your breath waiting for the planner to do this
for you.

regards, tom lane


-- 
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance


Re: [PERFORM] Efficiently merging and sorting collections of sorted rows

2017-06-23 Thread Peter Geoghegan
On Fri, Jun 23, 2017 at 3:58 PM, Clint Miller  wrote:
> 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.

It's not sorting stuff that doesn't need to be read into memory in the
first place. In the case of your plan with the sequential scan, some
rows are eliminated early, before being input to the sort node.

> 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?

I would like that too. There is a patch that does what I think you're
describing, but it seems to be in limbo:

https://commitfest.postgresql.org/11/409/

-- 
Peter Geoghegan


-- 
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance


[PERFORM] Efficiently merging and sorting collections of sorted rows

2017-06-23 Thread Clint Miller
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=101.16..101.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.


[PERFORM] Fwd: Slow query from ~7M rows, joined to two tables of ~100 rows each

2017-06-23 Thread Chris Wilson
Dear pgsql-performance list,

I think I've found a case where the query planner chooses quite a
suboptimal plan for joining three tables. The main "fact" table
(metric_value) links to two others with far fewer rows (like an OLAP/star
design). We retrieve and summarise a large fraction of rows from the main
table, and sort according to an index on that table, and I'd like to speed
it up, since we will need to run this query many times per day. I would
really appreciate your advice, thank you in advance!

The following SQL creates test data which can be used to reproduce the
problem:

drop table if exists metric_pos;
create table metric_pos (id serial primary key, pos integer);
insert into metric_pos (pos) SELECT (random() * 1000)::integer from
generate_series(1,100);
create index idx_metric_pos_id_pos on metric_pos (id, pos);

drop table if exists asset_pos;
create table asset_pos (id serial primary key, pos integer);
insert into asset_pos (pos) SELECT (random() * 1000)::integer from
generate_series(1,100);

drop TABLE if exists metric_value;
CREATE TABLE metric_value
(
  id_asset integer NOT NULL,
  id_metric integer NOT NULL,
  value double precision NOT NULL,
  date date NOT NULL,
  timerange_transaction tstzrange NOT NULL,
  id bigserial NOT NULL,
  CONSTRAINT cons_metric_value_pk PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);

insert into metric_value (id_asset, id_metric, date, value,
timerange_transaction)
select asset_pos.id, metric_pos.id, generate_series('2015-06-01'::date,
'2017-06-01'::date, '1 day'), random() * 1000, tstzrange(current_timestamp,
NULL)
from metric_pos, asset_pos;

CREATE INDEX idx_metric_value_id_metric_id_asset_date ON metric_value
(id_metric, id_asset, date, timerange_transaction, value);


This is an example of the kind of query we would like to speed up:

SELECT metric_pos.pos AS pos_metric, asset_pos.pos AS pos_asset, date,
value
FROM metric_value
INNER JOIN asset_pos ON asset_pos.id = metric_value.id_asset
INNER JOIN metric_pos ON metric_pos.id = metric_value.id_metric
WHERE
date >= '2016-01-01' and date < '2016-06-01'
AND timerange_transaction @> current_timestamp
ORDER BY metric_value.id_metric, metric_value.id_asset, date


This takes ~12 seconds from psql. Wrapping it in "SELECT SUM(value) FROM
(...) AS t" reduces that to ~8 seconds, so the rest is probably data
transfer overhead which is unavoidable.

The actual query plan selected is (explain.depesz.com
):

 Sort  (cost=378949.08..382749.26 rows=1520071 width=28) (actual
time=7917.686..8400.254 rows=152 loops=1)
   Sort Key: metric_value.id_metric, metric_value.id_asset,
metric_value.date
   Sort Method: external merge  Disk: 62408kB
   Buffers: shared hit=24421 read=52392, temp read=7803 written=7803
   ->  Hash Join  (cost=3.31..222870.41 rows=1520071 width=28) (actual
time=0.295..6049.550 rows=152 loops=1)
 Hash Cond: (metric_value.id_asset = asset_pos.id)
 Buffers: shared hit=24421 read=52392
 ->  Nested Loop  (cost=0.56..201966.69 <056%202019%206669>
rows=1520071 width=24) (actual time=0.174..4671.452 <01744%20671452>
rows=152 loops=1)
   Buffers: shared hit=24420 read=52392
   ->  Seq Scan on metric_pos  (cost=0.00..1.50 rows=100
width=8) (actual time=0.015..0.125 rows=100 loops=1)
 Buffers: shared hit=1
   ->  Index Only Scan using
idx_metric_value_id_metric_id_asset_date
on metric_value  (cost=0.56..1867.64 rows=15201 width=20) (actual
time=0.090..40.978 rows=15200 loops=100)
 Index Cond: ((id_metric = metric_pos.id) AND (date >=
'2016-01-01'::date) AND (date < '2016-06-01'::date))
 Filter: (timerange_transaction @> now())
 Heap Fetches: 152
 Buffers: shared hit=24419 read=52392
 ->  Hash  (cost=1.50..1.50 rows=100 width=8) (actual
time=0.102..0.102 rows=100 loops=1)
   Buckets: 1024  Batches: 1  Memory Usage: 12kB
   Buffers: shared hit=1
   ->  Seq Scan on asset_pos  (cost=0.00..1.50 rows=100
width=8) (actual time=0.012..0.052 rows=100 loops=1)
 Buffers: shared hit=1
 Planning time: 1.498 ms
 Execution time: 8992.846 ms


Or visually:

[image: Inline images 2]

What I find interesting about this query plan is:

The records can already be read in order from idx_metric_value If this
was selected as the primary table, and metric_pos was joined to it, then
the output would also be in order, and no sort would be needed.

We should be able to use a merge join to metric_pos, because it can be read
in order of id_metric (its primary key, and the first column in
idx_metric_value...). If not, a hash join should be faster than a nested
loop, if we only have to hash ~100 records.

I think that the joins should be fairly trivial: easily held in memory and
indexed by relatively small integers. They would probably be temporary
tables in our real use 

Re: [PERFORM] Dataset is fetched from cache but still takes same time to fetch records as first run

2017-06-23 Thread l...@laurent-hasson.com
ditto here... much slower, and crashes too often. We run an evergreen shop 
where I work, but everyone has moved back to III.

Sent from my BlackBerry KEYone - the most secure mobile device
From: adambrusselb...@gmail.com
Sent: June 23, 2017 8:11 AM
To: t...@sss.pgh.pa.us
Cc: sumeet.k.shu...@gmail.com; dstibr...@gmail.com; 
pgsql-performance@postgresql.org
Subject: Re: [PERFORM] Dataset is fetched from cache but still takes same time 
to fetch records as first run


On Fri, Jun 23, 2017 at 12:50 AM, Tom Lane 
> wrote:
It's possible that pgAdmin4 has improved matters in this area.

Sadly, not in my experience.  It's actually considerably worse than pgAdminIII 
in my experience when selecting a lot of rows, especially when very wide (20+ 
columns).


Re: [PERFORM] Inappropriate inner table for nested loop join

2017-06-23 Thread Albe Laurenz
Akihiko Odaki wrote:
> On 2017-06-23 20:20, Albe Laurenz wrote:
>> You could either try to do something like
>>
>> SELECT *
>> FROM (SELECT "posts".*
>>FROM "posts"
>>   JOIN "follows" ON "follows"."target_account" = "posts"."account"
>>WHERE "follows"."owner_account" = $1
>>OFFSET 0) q
>> ORDER BY "posts"."timestamp"
>> LIMIT 100;
> 
> Now I wonder whether it actually sorted or not. As you said, I want to
> "find rows with the greatest 'timestamp', match with rows from 'posts'
> in a nested loop and stop as soon as it has found 100 matches".
> 
> However, it seems to query 100 records without any consideration for
> "timestamp", and then sorts them. That is not expected. Here is a
> abstract query plan:
> 
>   Limit
> ->  Sort
>   Sort Key: posts.id DESC
>   ->  Nested Loop
> ->  Seq Scan on follows
>   Filter: (owner_account = $1)
> ->  Index Scan using index_posts_on_account on posts
>   Index Cond: (account_id = follows.target_account)
> 
> index_posts_on_account is an obsolete index on "posts" and only for
> "account". So it does nothing for sorting "timestamp".

Yes, if you replace posts.timestamp with q.timestamp, it should
sort by that.

Could you send CREATE TABLE and CREATE INDEX statements so I can try it?

Yours,
Laurenz Albe

-- 
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance


Re: [PERFORM] Dataset is fetched from cache but still takes same time to fetch records as first run

2017-06-23 Thread Adam Brusselback
On Fri, Jun 23, 2017 at 12:50 AM, Tom Lane  wrote:
>
> It's possible that pgAdmin4 has improved matters in this area.
>

Sadly, not in my experience.  It's actually considerably worse than
pgAdminIII in my experience when selecting a lot of rows, especially when
very wide (20+ columns).


Re: [PERFORM] Inappropriate inner table for nested loop join

2017-06-23 Thread Akihiko Odaki

On 2017-06-23 20:20, Albe Laurenz wrote:

You could either try to do something like

SELECT *
FROM (SELECT "posts".*
   FROM "posts"
  JOIN "follows" ON "follows"."target_account" = 

"posts"."account"

   WHERE "follows"."owner_account" = $1
   OFFSET 0) q
ORDER BY "posts"."timestamp"
LIMIT 100;


Now I wonder whether it actually sorted or not. As you said, I want to 
"find rows with the greatest 'timestamp', match with rows from 'posts' 
in a nested loop and stop as soon as it has found 100 matches".


However, it seems to query 100 records without any consideration for 
"timestamp", and then sorts them. That is not expected. Here is a 
abstract query plan:


 Limit
   ->  Sort
 Sort Key: posts.id DESC
 ->  Nested Loop
   ->  Seq Scan on follows
 Filter: (owner_account = $1)
   ->  Index Scan using index_posts_on_account on posts
 Index Cond: (account_id = follows.target_account)

index_posts_on_account is an obsolete index on "posts" and only for 
"account". So it does nothing for sorting "timestamp".


Regards,
Akihiko Odaki


--
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance


Re: [PERFORM] Inappropriate inner table for nested loop join

2017-06-23 Thread Akihiko Odaki

Thank you for your quick reply. Your solution works for me!

On 2017-06-23 20:20, Albe Laurenz wrote:
> PostgreSQL`s plan is to use the index on "posts"."timestamp" to find the
> rows with the lowest "timestamp", match with rows from "posts" in
> a nested loop and stop as soon as it has found 100 matches.
>
> Now it must be that the rows in "posts" that match with rows in "follows"
> have high values of "timestamp".

I mistakenly dropped DESC. The actual query should be:

SELECT "posts".*
  FROM "posts"
  JOIN "follows" ON "follows"."target_account" = "posts"."account"
  WHERE "follows"."owner_account" = $1
  ORDER BY "posts"."timestamp" DESC
  LIMIT 100

I note that here since that may be confusion to understand the later 
part of my first post.


> PostgreSQL doesn't know that, because it has no estimates how
> values correlate across tables, so it has to scan much more of the index
> than it had expected to, and the query performs poorly.

That is exactly the problem what I have encountered.

> You could either try to do something like
>
> SELECT *
> FROM (SELECT "posts".*
>FROM "posts"
>   JOIN "follows" ON "follows"."target_account" = 
"posts"."account"

>WHERE "follows"."owner_account" = $1
>OFFSET 0) q
> ORDER BY "posts"."timestamp"
> LIMIT 100;

It works. I had to replace "posts"."timestamp" with "timestamp", but 
that is trivial. Anything else is fine.


> Or you could frop the index on "posts"."timestamp" and see if that helps.

That is not a solution for me because it was used by other queries, but 
may make sense in other cases.



--
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance


Re: [PERFORM] Inappropriate inner table for nested loop join

2017-06-23 Thread Albe Laurenz
Akihiko Odaki wrote:
> I am having a problem with nested loop join.
> 
> A database has 2 tables: "posts" and "follows".
> Table "posts" have two columns: "timestamp" and "account".
> Table "follows" have two columns: "target_account" and "owner_account".
> The database also has an index on "posts" ("account", "timestamp"), one
> on "posts"("timestamp") and on "follows" ("owner_account",
> "target_account").
> 
> Table "posts" is so big and have 10 million records.
> The number of Records with the same value for "owner_accounts" in table
> "follows" is about 100 by average.
> 
> I issue the following query:
> 
> SELECT "posts".*
>FROM "posts"
>JOIN "follows" ON "follows"."target_account" = "posts"."account"
>WHERE "follows"."owner_account" = $1
>ORDER BY "posts"."timestamp"
>LIMIT 100
> 
> That results in a nested loop join with table "posts" as the inner and
> "follows" as the outer, which queried for each loop. EXPlAIN ANALYZE
> says the actual number of rows queried from table "posts" is 500,000.
> This behavior is problematic.
> 
> For performance, it may be better to retrieve 100 records joined with a
> record in table "follows", and then to retrieve those whose
> "posts"."timestamp" is greater than the one of last record we already
> have, or 100 records, joined with another record in table "follows", and
> so on. It would end up querying 10,000 records from table "posts" at
> most. The number could be even smaller in some cases.
> 
> Now I have these tough questions:
> * Is the "ideal" operation I suggested possible for PostgreSQL?
> * If so, I think that could be achieved by letting PostgreSQL use
> "follows" as the inner in the loops. How could I achieve that?
> * Is there any other way to improve the performance of the query?

PostgreSQL`s plan is to use the index on "posts"."timestamp" to find the
rows with the lowest "timestamp", match with rows from "posts" in
a nested loop and stop as soon as it has found 100 matches.

Now it must be that the rows in "posts" that match with rows in "follows"
have high values of "timestamp".

PostgreSQL doesn't know that, because it has no estimates how
values correlate across tables, so it has to scan much more of the index
than it had expected to, and the query performs poorly.

You could either try to do something like

SELECT *
FROM (SELECT "posts".*
  FROM "posts"
 JOIN "follows" ON "follows"."target_account" = "posts"."account"
  WHERE "follows"."owner_account" = $1
  OFFSET 0) q
ORDER BY "posts"."timestamp"  
LIMIT 100;

Or you could frop the index on "posts"."timestamp" and see if that helps.

Yours,
Laurenz Albe

-- 
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance


[PERFORM] Inappropriate inner table for nested loop join

2017-06-23 Thread Akihiko Odaki

Hi all,

I am having a problem with nested loop join.

A database has 2 tables: "posts" and "follows".
Table "posts" have two columns: "timestamp" and "account".
Table "follows" have two columns: "target_account" and "owner_account".
The database also has an index on "posts" ("account", "timestamp"), one 
on "posts"("timestamp") and on "follows" ("owner_account", 
"target_account").


Table "posts" is so big and have 10 million records.
The number of Records with the same value for "owner_accounts" in table 
"follows" is about 100 by average.


I issue the following query:

SELECT "posts".*
  FROM "posts"
  JOIN "follows" ON "follows"."target_account" = "posts"."account"
  WHERE "follows"."owner_account" = $1
  ORDER BY "posts"."timestamp"
  LIMIT 100

That results in a nested loop join with table "posts" as the inner and 
"follows" as the outer, which queried for each loop. EXPlAIN ANALYZE 
says the actual number of rows queried from table "posts" is 500,000. 
This behavior is problematic.


For performance, it may be better to retrieve 100 records joined with a 
record in table "follows", and then to retrieve those whose 
"posts"."timestamp" is greater than the one of last record we already 
have, or 100 records, joined with another record in table "follows", and 
so on. It would end up querying 10,000 records from table "posts" at 
most. The number could be even smaller in some cases.


Now I have these tough questions:
* Is the "ideal" operation I suggested possible for PostgreSQL?
* If so, I think that could be achieved by letting PostgreSQL use 
"follows" as the inner in the loops. How could I achieve that?

* Is there any other way to improve the performance of the query?

Answers are greatly appreciated.

Regards,
Akihiko Odaki


--
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance


Re: [PERFORM] Dataset is fetched from cache but still takes same time to fetch records as first run

2017-06-23 Thread Glyn Astill
>From: Tom Lane 
>To: Sumeet Shukla 
>Cc: Dave Stibrany ; pgsql-performance@postgresql.org
>Sent: Friday, 23 June 2017, 5:50
>Subject: Re: [PERFORM] Dataset is fetched from cache but still takes same time 
>to fetch records as first run
> Sumeet Shukla  writes:>
>> Yes, but when I actually execute the query in pgAdmin3, it takes exactly
>> the same time of 19.5 secs.
>
>pgAdmin is well known to be horribly inefficient at displaying large
>query results (and 121788 rows qualifies as "large" for this purpose,
>I believe).  The circa-tenth-of-a-second savings on the server side
>is getting swamped by client-side processing.
>
>It's possible that pgAdmin4 has improved matters in this area.

>

It's also possibly time taken for the results to be tranferred over a network 
if the data is large.

Glyn


-- 
Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-performance