aggregation on Multiple LEFT JOIN performance issue (SQL SERVER) - sql-server

I have a performance issue regarding a quite simple query that run for more than 30 minutes:
SELECT P.pID
,COUNT(T1.ID) AS NB1
,COUNT(T2.ID) AS NB2
,COUNT(T3.ID) AS NB3
,COUNT(T4.ID) AS NB4
,COUNT(T5.ID) AS NB5
FROM MainTable P
LEFT OUTER JOIN Table1 T1 ON P.pID = T1.pID
LEFT OUTER JOIN Table2 T2 ON P.pID = T2.pID
LEFT OUTER JOIN Table3 T3 ON P.pID = T3.pID
LEFT OUTER JOIN Table4 T4 ON P.pID = T4.pID
LEFT OUTER JOIN Table5 T5 ON P.pID = T5.pID
GROUP BY P.pID
Where as each query would reply in few ms:
ex.
SELECT P.pID
,COUNT(T1.ID) AS NB1
FROM MainTable P
LEFT OUTER JOIN Table1 T1 ON P.pID = T1.pID
GROUP BY P.pID
If I don't use any aggregation (COUNT or anything else) the query run in few ms:
ex.
SELECT P.pID
FROM MainTable P
LEFT OUTER JOIN Table1 T1 ON P.pID = T1.pID
LEFT OUTER JOIN Table2 T2 ON P.pID = T2.pID
LEFT OUTER JOIN Table3 T3 ON P.pID = T3.pID
LEFT OUTER JOIN Table4 T4 ON P.pID = T4.pID
LEFT OUTER JOIN Table5 T5 ON P.pID = T5.pID
GROUP BY P.pID
Obviously all indexes are set etc...
The only "slowing down" element is that pID is a varchar (50) but I can't change it and in my opinion this is not be the main problem here.
I used a workaround including union all that work fine but I really wondering why these is so long and how could I optimize this as aggregating over multiple left join are really common stuff in reporting project and should not be so slow.
thank you for your help.
[EDIT]
thx to ARION i got a nice query working really nice.
But my main concern is about understanding what's wrong in the sql engine writing the query with multiple left join.
Tables descr would be :
Table P (500 rows)
pID varchar(50) NOT NULL as primary key
p.* doesn't matter
Table Tn (between 2000 and 8000 rows)
Tn.ID int NOT NULL as primary key
pID varchar(50) NOT NULL as Foreign key
[EDIT] Thanks to Erland Sommarskog on social.msdn.microsoft.com that point me my error of analyse.
-- detail about the answer
Keep in mind :
LEFT JOIN forms cartesian product
I was wrong assuming that the Cartesian product might have been filtered as I always refere to the same table.
thanks

Maybe something like this:
SELECT
P.pID,
(SELECT COUNT(*) FROM Table1 T1 WHERE P.pID = T1.pID) AS NB1,
(SELECT COUNT(*) FROM Table2 T2 WHERE P.pID = T2.pID) AS NB2,
(SELECT COUNT(*) FROM Table3 T3 WHERE P.pID = T3.pID) AS NB3,
(SELECT COUNT(*) FROM Table4 T4 WHERE P.pID = T4.pID) AS NB4,
(SELECT COUNT(*) FROM Table5 T5 WHERE P.pID = T5.pID) AS NB5
FROM MainTable P

You could also rewrite the query by first grouping by (in subqueries), then joining:
SELECT
P.pID,
T1.NB1,
T2.NB2,
T3.NB3,
T4.NB4,
T5.NB5
FROM MainTable P
LEFT JOIN
(SELECT pID, COUNT(*) AS NB1 FROM Table1 GROUP BY pID) AS T1
ON T1.pID = P.pID
LEFT JOIN
(SELECT pID, COUNT(*) AS NB2 FROM Table2 GROUP BY pID) AS T2
ON T2.pID = P.pID
LEFT JOIN
(SELECT pID, COUNT(*) AS NB3 FROM Table3 GROUP BY pID) AS T3
ON T3.pID = P.pID
LEFT JOIN
(SELECT pID, COUNT(*) AS NB4 FROM Table4 GROUP BY pID) AS T4
ON T4.pID = P.pID
LEFT JOIN
(SELECT pID, COUNT(*) AS NB5 FROM Table5 GROUP BY pID) AS T5
ON T5.pID = P.pID
This would be useful if you want to include in the results other aggregates, besides the COUNT(*), without having to run more correlated subqueries.

Related

MSSQL Query framing

I have 3 tables like master and 2 child tables . I am using join condition but did not get as expected client needs.
Query:
select * from(
select a.id as mid,b.id,b.val from ##mastertable a right join ##table1 b
on a.id=b.id ) as c inner join ##table2 d on c.mid=d.id
Kindly provide any other way to get proper result.
Try:
Select a.id, b.id, b.val
From (mastertable a
Right Join table1 b
On a.id = b.id)
Inner Join table2 d
On a.id = d.id;
This should work :
select * from (
(select master.id from master_table) master
LEFT OUTER JOIN
(select table1.id, table1.value from table_1) table1
ON
master.id = table1.id
FULL OUTER JOIN
(select table2.id, table2.value from table_2) table2
ON
table1.id = table2.id
AND
table1.val = table2.val
)
;
Try below: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=2b58625961c444997829abb45d40417b
select * from master m inner join
(select table2.id as id1, table2.value as val1, table1.value as val2, table1.id as id2
from table2 left join table1
on table1.id=table2.id and table1.value=table2.value)x on m.id=x.id1
This will be same as you expected
select
m.id, t1.id, t1.value, t2.id, t2.value
from
m
left join
t1
on
m.id = t1.id
left join
t2
on
t1.id = t2.id and t1.value = t2.value
order by m.id, t1.value
here M is your master table, T1 is table1 and t2 is table2

SQL query - combine results from 2 SELECT join and result should be all distinct rows

Query #1:
Select t1.*
From t1
Join t2 on t1.c1 = t2.c1
Query #2:
Select t1.*
from t1
join t3 on t1.c2 = t3.c2
I would like to combine the results of above 2 select queries and not get any duplicates. I am doing an UNION but it is taking forever to execute
select t1.*
from t1
where
exists (
select t2.c1
from t2
where
t2.c1 = t1.c1
)
or exists (
select t3.c2
from t3
where
t3.c2 = t1.c2
)
or, if you need values from t2 and/or t3:
select t1.*
from t1
left outer join t2 on
t2.c1 = t1.c1
left outer join t3 on
t3.c2 = t1.c2
where
t2.c1 is not null
or t3.c2 is not null
You can try each to see if you get acceptable performance (I don't think you will based on your UNION experience). If it is slow, you should consider putting a indexes on c1 and c2 in both tables, preferably primary keys on the t2 and t3 tables, and then a non-clustered index on each of c1 and c2 on t1.
Amount of time will depend on several aspects of your environment.
Another way to return t1.* would be:
select distinct t1.*
from t1 a
join t2 b
on a.c1 = b.c1
join t3 c
on a.c2 = c.c2
Distinct will sort in tempdb (takes time)

What type of SQL join is it in which the inner-join is not directly followed by an ON clause?

What is the name of this type of join? I've looked all over! The query is inner-joining back-to-back and then specifying two ON clauses.
Bonus points: What is the benefit of joining this way?
SELECT
<some columns>
FROM
ProductTypes AS t0
INNER JOIN Table1 AS t1
INNER JOIN Table2 AS t2
ON t2.CodeId = t1.CodeId
AND t2.[Enabled] = 1
ON t1.ClassId = t0.ClassId
Still it will be INNER JOIN.
It will interpreted as
SELECT <some columns>
FROM producttypes AS t0
INNER JOIN table2 AS t2
ON t1.classid = t0.classid
INNER JOIN table1 AS t1
ON t2.codeid = t1.codeid
AND t2.[enabled] = 1
Compiler is smart enough to rearrange the JOIN order
Here is demo of what's happening internally. I have used my own table's with similar JOIN order
Your query JOIN order
SELECT
*
FROM
users AS t0
INNER JOIN products AS t1
INNER JOIN orders AS t2
ON t2.productid = t1.productid
AND t2.productid = 1
ON t2.uid = t0.uid
Execution Plan
The rearranged JOIN order
SELECT
*
FROM
users AS t0
INNER JOIN orders AS t2
ON t2.uid = t0.uid
INNER JOIN products AS t1
ON t2.productid = t1.productid
AND t2.productid = 1
Execution Plan
As you can see both has identical execution plan. So there wont be any difference
This is a form of doing nested joins. There's no purpose with all inner unless you want to do it for readability. It can be useful if you use left outer. Consider:
SELECT
<some columns>
FROM
ProductTypes AS t0
LEFT OUTER JOIN Table1 AS t1
INNER JOIN Table2 AS t2
ON t2.CodeId = t1.CodeId
AND t2.[Enabled] = 1
ON t1.ClassId = t0.ClassId
Now this does something. If you did it without putting the t1/t0 at the end the t1/t2 inner would basically negate the fact that t1/t0 is a left outer. So doing it this way lets you have t0 records with no t1 records (just like a normal left outer join), but will only show the t1 records that ALSO have a t2. The inner join is enforced at this lower level.
See also:
http://sqlity.net/en/1435/a-join-a-day-nested-joins/
SQL join format - nested inner joins

How to get Count of Joined Rows

There is this SQL Statement
SELECT t1.Name
,Count(t2.SubID) Totals -- I don't know how to do it.
FROM Table t1
INNER JOIN Table2 t2 ON t1.ID = t2.SubID
Thanks.
Guessing... change the JOIN and add GROUP BY
SELECT t1.Name
,Count(t2.SubID) AS Totals
FROM Table t1
LEFT OUTER JOIN Table2 t2 ON t1.ID = t2.SubID
GROUP BY t1.Name
The LEFT OUTER JOIN allows you to find count zero rows per t1.Name
SELECT t1.Name, Count(t2.SubID) Totals
FROM Table t1
INNER JOIN Table2 t2 ON t1.ID = t2.SubID
GROUP BY t1.Name

Change Sql "Not In" To "Left Outer Join"

I don't want to use "not in" this sql query. How can I do it? thanks
SELECT
T2.Sno,
T2.Name,
T1.description,
T2.UserCode
FROM
Table1 AS T1 (nolock)
INNER JOIN T2 (nolock)
ON T1.UserCode = T2.UserCode
WHERE
g.xid= #p_xid
and T2.Sno not in (select Gid from T3 (nolock))
Assuming there is no row in T2 where Sno is null and in T3 where Gid is null:
SELECT
T2.Sno,
T2.Name,
T1.description,
T2.UserCode
FROM
Table1 AS T1 WITH (nolock)
INNER JOIN T2 WITH (nolock)
LEFT JOIN T3 WITH (NOLOCK)
ON T2.Sno = T3.Gid
ON T1.UserCode = T2.UserCode
WHERE
g.xid= #p_xid
and T3.Gid IS NULL
If you have multiple T3 rows per T2.Sno = T3.Gid, you'll need DISTINCT in a JOIN.
Without DISTINCT, it's a different query
With DISTINCT, it's an extra step.
I'd use NOT EXISTS which avoids this.
SELECT
T2.Sno,
T2.Name,
T1.description,
T2.UserCode
FROM
Table1 AS T1 (nolock)
INNER JOIN T2 (nolock)
ON T1.UserCode = T2.UserCode
WHERE
g.xid= #p_xid
and not exists (select * from T3 (nolock) where T3.Gid = T2.Sno)

Resources