Skip to content

[improvement](fe) Convert inner join to semi join when right side is an existence filter - #66535

Open
englefly wants to merge 1 commit into
apache:masterfrom
englefly:inner-semi
Open

[improvement](fe) Convert inner join to semi join when right side is an existence filter#66535
englefly wants to merge 1 commit into
apache:masterfrom
englefly:inner-semi

Conversation

@englefly

@englefly englefly commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: N/A

Problem Summary: For queries like
select distinct a1.* from a1, a5 where a1.lot_id = a5.lot_id and a1.ope_no = a5.ope_no and ...
where the right side table of an inner join only appears in equal join conditions
(never in the output or any other expression), the inner join acts purely as an
existence filter: it only decides which left rows are kept. Such an inner join can
be safely rewritten to a left semi join, which keeps the output row count at the
left side cardinality instead of multiplying it by the average number of right side
matches, and lets the right side scan/broadcast only the join key columns.

The conversion is guarded by three conditions, all checked by the new
ConvertInnerJoinToSemiJoin RBO rule:

  1. the right side columns of the join are not referenced above the join
    (the aggregate/project above only consumes left side columns);
  2. all join conditions are equal conjuncts (hashJoinConjuncts non-empty,
    otherJoinConjuncts empty; <=> NullSafeEqual is also covered since it is an
    EqualPredicate extracted into hashJoinConjuncts by FindHashConditionForJoin);
  3. there is a deduplication guarantee above the join (a DISTINCT-like aggregate
    whose group-by keys cover exactly its output columns), so the row multiplication
    of an inner join does not change the final result.

The rule is registered in the RBO rewrite phase "eliminate join according unique or
foreign key" (after infer predicate / push down distinct, before push down limit).

Release note

None

Check List (For Author)

  • Test: Unit Test (ConvertInnerJoinToSemiJoinTest, 6 cases: convert with DISTINCT,
    convert with <=>, not convert when right columns leak / no distinct / non-equi
    condition / aggregate function; sibling join-rule tests pass, no regression)
  • Behavior changed: No
  • Does this need documentation: No

…an existence filter

### What problem does this PR solve?

Issue Number: N/A

Problem Summary: For queries like
`select distinct a1.* from a1, a5 where a1.lot_id = a5.lot_id and a1.ope_no = a5.ope_no and ...`
where the right side table of an inner join only appears in equal join conditions
(never in the output or any other expression), the inner join acts purely as an
existence filter: it only decides which left rows are kept. Such an inner join can
be safely rewritten to a left semi join, which keeps the output row count at the
left side cardinality instead of multiplying it by the average number of right side
matches, and lets the right side scan/broadcast only the join key columns.

The conversion is guarded by three conditions, all checked by the new
`ConvertInnerJoinToSemiJoin` RBO rule:
1. the right side columns of the join are not referenced above the join
   (the aggregate/project above only consumes left side columns);
2. all join conditions are equal conjuncts (hashJoinConjuncts non-empty,
   otherJoinConjuncts empty; `<=>` NullSafeEqual is also covered since it is an
   EqualPredicate extracted into hashJoinConjuncts by FindHashConditionForJoin);
3. there is a deduplication guarantee above the join (a DISTINCT-like aggregate
   whose group-by keys cover exactly its output columns), so the row multiplication
   of an inner join does not change the final result.

The rule is registered in the RBO rewrite phase "eliminate join according unique or
foreign key" (after infer predicate / push down distinct, before push down limit).

### Release note

None

### Check List (For Author)

- Test: Unit Test (ConvertInnerJoinToSemiJoinTest, 6 cases: convert with DISTINCT,
  convert with `<=>`, not convert when right columns leak / no distinct / non-equi
  condition / aggregate function; sibling join-rule tests pass, no regression)
- Behavior changed: No
- Does this need documentation: No
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@englefly

englefly commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 29182 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 8e53d538986e4e4d296fab97edd3dbd2bd0f3a5c, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17652	3973	3975	3973
q2	2023	329	210	210
q3	10265	1424	793	793
q4	4677	469	341	341
q5	7552	911	565	565
q6	187	167	132	132
q7	734	794	599	599
q8	9352	1486	1539	1486
q9	5323	4059	4081	4059
q10	6732	1616	1356	1356
q11	524	348	339	339
q12	732	566	447	447
q13	18112	3280	2754	2754
q14	265	263	248	248
q15	q16	736	736	659	659
q17	1035	942	1010	942
q18	6688	5615	5539	5539
q19	1172	1266	1082	1082
q20	807	685	600	600
q21	6406	2811	2751	2751
q22	451	379	307	307
Total cold run time: 101425 ms
Total hot run time: 29182 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	5014	4605	4679	4605
q2	286	332	202	202
q3	4899	5220	4677	4677
q4	2191	2291	1420	1420
q5	4805	4681	4468	4468
q6	229	173	125	125
q7	1852	1700	1485	1485
q8	2370	2045	2055	2045
q9	7244	6955	6732	6732
q10	4254	4238	3809	3809
q11	508	374	338	338
q12	692	700	497	497
q13	3020	3220	2763	2763
q14	276	284	248	248
q15	q16	656	673	588	588
q17	1228	1227	1202	1202
q18	12145	11005	11839	11005
q19	1107	1094	1057	1057
q20	2187	2212	1915	1915
q21	5250	4491	4536	4491
q22	521	470	429	429
Total cold run time: 60734 ms
Total hot run time: 54101 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 166473 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 8e53d538986e4e4d296fab97edd3dbd2bd0f3a5c, data reload: false

query5	4300	589	437	437
query6	478	212	212	212
query7	4920	609	341	341
query8	318	172	151	151
query9	8746	3994	4026	3994
query10	456	340	283	283
query11	5833	2206	1978	1978
query12	156	100	103	100
query13	1271	634	425	425
query14	6125	4264	4024	4024
query14_1	3821	3797	3829	3797
query15	205	206	176	176
query16	972	472	453	453
query17	946	672	530	530
query18	2450	464	327	327
query19	195	182	141	141
query20	104	102	99	99
query21	223	157	133	133
query22	13022	13015	12760	12760
query23	15899	15153	14728	14728
query23_1	14581	14708	14628	14628
query24	7589	1667	1219	1219
query24_1	1220	1239	1224	1224
query25	552	424	349	349
query26	1299	369	198	198
query27	2594	614	386	386
query28	4559	2031	1998	1998
query29	1049	640	470	470
query30	356	262	226	226
query31	1183	1112	1035	1035
query32	102	64	61	61
query33	506	300	233	233
query34	1181	1085	622	622
query35	731	746	638	638
query36	772	769	705	705
query37	417	449	382	382
query38	1849	1793	1660	1660
query39	821	816	800	800
query39_1	790	786	791	786
query40	251	169	144	144
query41	67	64	63	63
query42	93	90	92	90
query43	311	319	274	274
query44	1487	784	764	764
query45	189	172	166	166
query46	1038	1139	715	715
query47	1627	1566	1520	1520
query48	411	394	303	303
query49	578	401	316	316
query50	1044	443	349	349
query51	10635	10656	10346	10346
query52	90	91	76	76
query53	264	285	208	208
query54	301	252	231	231
query55	77	76	68	68
query56	324	311	294	294
query57	1058	999	984	984
query58	286	259	267	259
query59	1510	1613	1378	1378
query60	326	287	269	269
query61	172	171	177	171
query62	406	321	273	273
query63	238	203	199	199
query64	2987	1145	979	979
query65	3909	3832	3826	3826
query66	1856	502	380	380
query67	28137	28289	27459	27459
query68	3240	1615	1050	1050
query69	410	312	271	271
query70	887	800	794	794
query71	386	343	323	323
query72	3108	2607	2277	2277
query73	866	793	411	411
query74	4664	4480	4272	4272
query75	2377	2325	1970	1970
query76	2334	1175	775	775
query77	340	360	280	280
query78	11270	11201	10488	10488
query79	1382	1172	743	743
query80	1233	562	470	470
query81	538	331	287	287
query82	848	764	671	671
query83	380	336	305	305
query84	329	159	128	128
query85	988	611	552	552
query86	397	229	223	223
query87	1989	1947	1856	1856
query88	3704	2826	2773	2773
query89	398	326	278	278
query90	1890	196	193	193
query91	205	194	158	158
query92	62	61	53	53
query93	1661	1535	1010	1010
query94	715	343	311	311
query95	785	520	480	480
query96	1121	793	367	367
query97	2456	2415	2302	2302
query98	190	192	181	181
query99	736	729	613	613
Total cold run time: 254196 ms
Total hot run time: 166473 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 23.71 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 8e53d538986e4e4d296fab97edd3dbd2bd0f3a5c, data reload: false

query1	0.00	0.00	0.00
query2	0.10	0.05	0.05
query3	0.25	0.13	0.13
query4	1.61	0.15	0.14
query5	0.24	0.22	0.21
query6	1.15	0.83	0.84
query7	0.04	0.01	0.01
query8	0.06	0.03	0.04
query9	0.39	0.31	0.31
query10	0.52	0.56	0.55
query11	0.19	0.13	0.14
query12	0.17	0.14	0.13
query13	0.45	0.46	0.46
query14	1.00	0.97	0.97
query15	0.59	0.58	0.58
query16	0.31	0.30	0.32
query17	1.08	1.11	1.05
query18	0.21	0.21	0.20
query19	2.05	1.97	1.91
query20	0.02	0.02	0.01
query21	15.44	0.22	0.12
query22	4.83	0.05	0.05
query23	16.13	0.31	0.12
query24	2.98	0.41	0.31
query25	0.10	0.05	0.05
query26	0.73	0.20	0.14
query27	0.05	0.04	0.03
query28	3.56	0.77	0.35
query29	12.50	4.05	3.25
query30	0.27	0.14	0.15
query31	2.76	0.55	0.31
query32	3.22	0.59	0.48
query33	3.28	3.14	3.11
query34	15.73	3.95	3.28
query35	3.23	3.24	3.21
query36	0.57	0.42	0.43
query37	0.09	0.06	0.07
query38	0.04	0.04	0.03
query39	0.04	0.03	0.03
query40	0.17	0.15	0.14
query41	0.09	0.04	0.03
query42	0.03	0.02	0.02
query43	0.04	0.03	0.03
Total cold run time: 96.31 s
Total hot run time: 23.71 s

@englefly

englefly commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 29124 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 8e53d538986e4e4d296fab97edd3dbd2bd0f3a5c, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17859	3964	3939	3939
q2	2037	334	216	216
q3	10230	1353	814	814
q4	4683	471	337	337
q5	7488	811	557	557
q6	182	172	143	143
q7	757	792	593	593
q8	9320	1429	1549	1429
q9	5283	4050	4067	4050
q10	6726	1609	1349	1349
q11	511	357	329	329
q12	719	580	449	449
q13	18092	3246	2778	2778
q14	263	251	243	243
q15	q16	730	726	663	663
q17	1007	995	1028	995
q18	6548	5620	5549	5549
q19	1298	1343	1143	1143
q20	807	687	542	542
q21	6284	2804	2685	2685
q22	473	377	321	321
Total cold run time: 101297 ms
Total hot run time: 29124 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	4979	4648	4640	4640
q2	306	324	210	210
q3	5009	5262	4713	4713
q4	2220	2226	1411	1411
q5	4698	4443	4406	4406
q6	241	180	127	127
q7	1818	1659	1546	1546
q8	2407	2021	1982	1982
q9	7147	6685	6692	6685
q10	4223	4222	3796	3796
q11	513	376	352	352
q12	692	706	494	494
q13	2950	3219	2740	2740
q14	275	286	252	252
q15	q16	657	691	592	592
q17	1237	1224	1207	1207
q18	12037	11048	11687	11048
q19	1115	1113	1059	1059
q20	2203	2171	1898	1898
q21	5258	4511	4566	4511
q22	516	468	406	406
Total cold run time: 60501 ms
Total hot run time: 54075 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 159320 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 8e53d538986e4e4d296fab97edd3dbd2bd0f3a5c, data reload: false

query5	4309	617	440	440
query6	460	215	213	213
query7	4853	597	343	343
query8	317	163	148	148
query9	8771	4036	4062	4036
query10	489	342	298	298
query11	5809	2189	2051	2051
query12	151	102	100	100
query13	1252	619	460	460
query14	6083	4333	4034	4034
query14_1	3854	3835	3820	3820
query15	214	206	174	174
query16	1002	500	478	478
query17	958	681	527	527
query18	2416	452	336	336
query19	221	184	140	140
query20	103	100	98	98
query21	233	158	133	133
query22	13115	12950	12675	12675
query23	15942	15042	14552	14552
query23_1	14565	14714	14586	14586
query24	7522	1676	1236	1236
query24_1	1260	1218	1261	1218
query25	533	419	356	356
query26	1316	358	213	213
query27	2647	592	389	389
query28	4560	2045	2006	2006
query29	1033	594	463	463
query30	342	263	225	225
query31	1166	1118	1040	1040
query32	103	60	58	58
query33	514	294	250	250
query34	1162	1114	647	647
query35	718	742	618	618
query36	785	759	704	704
query37	420	431	418	418
query38	1825	1768	1668	1668
query39	835	822	790	790
query39_1	770	787	775	775
query40	243	168	148	148
query41	66	62	64	62
query42	95	93	93	93
query43	321	316	277	277
query44	1455	766	788	766
query45	191	171	168	168
query46	1059	1162	701	701
query47	1521	1493	1418	1418
query48	410	401	301	301
query49	589	401	291	291
query50	1130	439	350	350
query51	10758	10571	10600	10571
query52	88	90	79	79
query53	270	278	198	198
query54	283	242	209	209
query55	78	74	71	71
query56	307	323	284	284
query57	1018	977	919	919
query58	298	268	285	268
query59	1541	1610	1417	1417
query60	345	304	282	282
query61	179	175	178	175
query62	415	350	273	273
query63	243	211	205	205
query64	2973	1144	989	989
query65	3905	3872	3830	3830
query66	1857	491	377	377
query67	20119	20192	19811	19811
query68	3134	1508	990	990
query69	409	306	281	281
query70	889	789	780	780
query71	356	343	314	314
query72	3151	2662	2392	2392
query73	883	829	441	441
query74	4621	4523	4285	4285
query75	2377	2339	2006	2006
query76	2316	1149	803	803
query77	349	368	285	285
query78	11130	11082	10584	10584
query79	1425	1140	710	710
query80	1249	545	478	478
query81	576	340	277	277
query82	851	768	683	683
query83	382	336	299	299
query84	329	170	132	132
query85	976	588	511	511
query86	395	237	229	229
query87	1993	1972	1849	1849
query88	3723	2795	2816	2795
query89	391	321	278	278
query90	1898	192	192	192
query91	204	188	165	165
query92	67	62	59	59
query93	1614	1518	985	985
query94	699	363	336	336
query95	797	509	584	509
query96	1013	829	354	354
query97	2476	2450	2313	2313
query98	205	188	188	188
query99	732	723	610	610
Total cold run time: 246204 ms
Total hot run time: 159320 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 23.9 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 8e53d538986e4e4d296fab97edd3dbd2bd0f3a5c, data reload: false

query1	0.00	0.01	0.00
query2	0.11	0.05	0.04
query3	0.26	0.14	0.14
query4	1.61	0.14	0.14
query5	0.25	0.22	0.22
query6	1.16	0.82	0.82
query7	0.04	0.01	0.01
query8	0.06	0.04	0.04
query9	0.38	0.32	0.31
query10	0.54	0.56	0.55
query11	0.19	0.14	0.14
query12	0.17	0.14	0.14
query13	0.45	0.45	0.46
query14	1.02	0.99	0.98
query15	0.60	0.58	0.60
query16	0.33	0.33	0.31
query17	1.16	1.09	1.11
query18	0.20	0.20	0.19
query19	2.04	2.01	2.00
query20	0.02	0.01	0.01
query21	15.44	0.22	0.15
query22	4.61	0.06	0.05
query23	16.12	0.30	0.12
query24	2.98	0.46	0.31
query25	0.14	0.05	0.05
query26	0.73	0.21	0.14
query27	0.03	0.03	0.04
query28	3.62	0.73	0.35
query29	12.49	4.03	3.16
query30	0.27	0.15	0.14
query31	2.78	0.55	0.30
query32	3.25	0.59	0.49
query33	3.23	3.17	3.18
query34	15.64	3.91	3.28
query35	3.20	3.22	3.22
query36	0.54	0.42	0.44
query37	0.09	0.06	0.06
query38	0.05	0.04	0.04
query39	0.04	0.03	0.03
query40	0.16	0.15	0.14
query41	0.09	0.03	0.03
query42	0.04	0.03	0.03
query43	0.05	0.03	0.03
Total cold run time: 96.18 s
Total hot run time: 23.9 s

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage 84.85% (28/33) 🎉
Increment coverage report
Complete coverage report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants