I have an query with an expensive SORT after the join: use tempdb drop table tempdb..t1 drop table tempdb..t2 drop table tempdb..t3 create table t1(c1 int, c2 int, c3 int, c4 int, c5 int) create table t2 (c1 int, c2 int, c3 int, c4 int, c5 int) create table t3 (c1 int, c2 int, c3 int, c4 int, c5 int) go set nocount off declare @x int =1 declare @max int =500000 while(@x<=@max) begin insert into t1 values(@x,@max-@x,(@max-@x)*10,@x*100,@x); insert into t2 values(@x,@x,@x,@x,@x); insert into t3 values(@x,@x,(@x)*10,@x,@x); set @x+=1; end go select * from t1 select * from t2 select * from t3 go create clustered index idxt1a on t1(c2,c4,c3,c5) create index idxt1b on t1(c3) include (c1) CREATE STATISTICS [_dta_stat_3_2_4_5] ON [dbo].[t1]([c3], [c2], [c4], [c5]) CREATE STATISTICS [_dta_stat_4_3_5] ON [dbo].[t1]([c4], [c3], [c5]) create index idxt2 on t2(c2) --ok create index idxt3 on t3(c3) --ok go set statistics io on set statistics time on set statistics profile on go select t1.* from t1 --WITH (INDEX (idxt1b)) inner /*merge*/ join t2 on t1.c2=t2.c2 inner join t3 on t1.c3=t3.c3 order by t1.c2, t1.c4,t1.c3,t1.c5 --option (FORCE ORDER) go set statistics io off set statistics time off set statistics profile off ---------- ---------- Ideally, I'd like to avoid the expensive in memory SORT, which is spilling into tempdb. ---------- ---------- Work done: 1. I tried various combinations of indexes to try to get an index intersection going - no luck 2. Also tried rewriting somehow to a union all - since previous query also returned duplicates - worse performance --- rewritten as union all ------------- set statistics io on set statistics time on set statistics profile on go select t1.* from t1 WITH (INDEX (idxt1a)) inner /*merge*/ join t2 on t1.c2=t2.c2 --order by t1.c2--,t1.c4,t1.c3,t1.c5 union all select t1.* from t1 WITH (INDEX (idxt1b)) inner /*merge*/ join t3 on t1.c3=t3.c3 order by t1.c2,t1.c4,t1.c3,t1.c5 --option (FORCE ORDER) -- WITH (INDEX (idxt1d)) --option (Table hint ( t1, INDEX(idxt1b))) go set statistics io off set statistics time off set statistics profile off 3. Rewrote to use Exists clause - not much better performance --- rewritten as EXISTS ------------- set statistics io on set statistics time on set statistics profile on go select t1.* from t1 --WITH (INDEX (idxt1b)) where exists (select c2 from t2 where t1.c2=t2.c2) and exists (select c3 from t3 where t1.c3=t3.c3) order by t1.c2, t1.c4,t1.c3,t1.c5 --option (FORCE ORDER) go set statistics io off set statistics time off set statistics profile off 4.--- rewritten as IN ------------- set statistics io on set statistics time on set statistics profile on go select t1.* from t1 --WITH (INDEX (idxt1b)) where t1.c2 in (select c2 from t2) and t1.c3 in (select c3 from t3) order by t1.c2, t1.c4,t1.c3,t1.c5 --option (FORCE ORDER) go set statistics io off set statistics time off set statistics profile off Any creative ideas of how to avoid the sort after the joins?
↧
How to avoid an expensive SORT after a JOIN?
↧