public inbox for [email protected]
help / color / mirror / Atom feedFrom: Andrey Lepikhov <[email protected]>
To: PostgreSQL-development <[email protected]>
Cc: Tom Lane <[email protected]>
Subject: MergeJoin beats HashJoin in the case of multiple hash clauses
Date: Thu, 15 Jun 2023 14:30:10 +0600
Message-ID: <[email protected]> (raw)
Hi, all.
Some of my clients use JOIN's with three - four clauses. Quite
frequently, I see complaints on unreasonable switch of JOIN algorithm to
Merge Join instead of Hash Join. Quick research have shown one weak
place - estimation of an average bucket size in final_cost_hashjoin (see
q2.sql in attachment) with very conservative strategy.
Unlike estimation of groups, here we use smallest ndistinct value across
all buckets instead of multiplying them (or trying to make multivariate
analysis).
It works fine for the case of one clause. But if we have many clauses,
and if each has high value of ndistinct, we will overestimate average
size of a bucket and, as a result, prefer to use Merge Join. As the
example in attachment shows, it leads to worse plan than possible,
sometimes drastically worse.
I assume, this is done with fear of functional dependencies between hash
clause components. But as for me, here we should go the same way, as
estimation of groups.
The attached patch shows a sketch of the solution.
--
regards,
Andrey Lepikhov
Postgres Professional
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index ef475d95a1..26f26d6a40 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -4033,11 +4033,12 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
thismcvfreq = restrictinfo->left_mcvfreq;
}
- if (innerbucketsize > thisbucketsize)
- innerbucketsize = thisbucketsize;
- if (innermcvfreq > thismcvfreq)
- innermcvfreq = thismcvfreq;
+ innerbucketsize *= thisbucketsize;
+ innermcvfreq *= thismcvfreq;
}
+
+ if (innerbucketsize > virtualbuckets)
+ innerbucketsize = 1.0 / virtualbuckets;
}
/*
Attachments:
[application/sql] q2.sql (1.9K, ../[email protected]/2-q2.sql)
download
[text/plain] fix_bucketsize.diff (674B, ../[email protected]/3-fix_bucketsize.diff)
download | inline diff:
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index ef475d95a1..26f26d6a40 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -4033,11 +4033,12 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
thismcvfreq = restrictinfo->left_mcvfreq;
}
- if (innerbucketsize > thisbucketsize)
- innerbucketsize = thisbucketsize;
- if (innermcvfreq > thismcvfreq)
- innermcvfreq = thismcvfreq;
+ innerbucketsize *= thisbucketsize;
+ innermcvfreq *= thismcvfreq;
}
+
+ if (innerbucketsize > virtualbuckets)
+ innerbucketsize = 1.0 / virtualbuckets;
}
/*
view thread (4+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected]
Subject: Re: MergeJoin beats HashJoin in the case of multiple hash clauses
In-Reply-To: <[email protected]>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox