How to improve query performance for multiple inner joins?

4.7k Views Asked by At

I have a query which is taking 1 minute to execute. After spending some time on the query I found there is some part that is actually causing the query to take the time. Please see my comments below for the query mentioned.

Complete query:

SELECT DISTINCT 
    CSU.*, U.txtFirstName, U.txtLastName 
FROM
    tblCRMShallowUsers CSU (NOLOCK) 
INNER JOIN 
    tblUsers U (NOLOCK) ON CSU.PK_autUserID = U.PK_autUserID 
INNER JOIN 
    tblUserGroupLink UGL (NOLOCK) ON U.PK_autUserID = UGL.FK_lngUsersID 
INNER JOIN 
    tblModuleSecurityLinks MSL (NOLOCK) ON FK_lngModuleID = 28 
INNER JOIN 
    tblGroups G (NOLOCK) ON G.PK_autGroupID = MSL.FK_lngGroupID 
WHERE 
    MSL.lngRights > 0
    AND U.lngStatus > 19
    AND U.ysnAdminFlag = 0
    AND G.lngStatus > 19
    AND G.ysnFrontEndGroup = 0
    AND (UGL.FK_lngGroupID = MSL.FK_lngGroupID
         OR UGL.FK_lngGroupID = 2) 
ORDER BY 
    ysnHasAccess DESC, txtLastName, txtFirstName

Below joins are performing quickly in the above query:

INNER JOIN 
    tblUsers U (NOLOCK) ON CSU.PK_autUserID = U.PK_autUserID 
INNER JOIN 
    tblUserGroupLink UGL (NOLOCK) ON U.PK_autUserID = UGL.FK_lngUsersID 
INNER JOIN 
    tblModuleSecurityLinks MSL (NOLOCK) ON FK_lngModuleID = 28 
INNER JOIN 
    tblGroups G (NOLOCK) ON G.PK_autGroupID = MSL.FK_lngGroupID 

Here's the AND part in the above query that is really slowing it down. When I remove this join it was working very fast but the result set is not the result that was coming earlier.(it returns more data)

AND (UGL.FK_lngGroupID = MSL.FK_lngGroupID
     OR UGL.FK_lngGroupID = 2)

I would really appreciate if you can show some direction to optimize the query or some example or other way of writing the same query.

2

There are 2 best solutions below

0
On BEST ANSWER

Because the optimal execution plan with each of the OR predicates differs, performance is improved by refactoring the single query as separate SELECT queries and a UNION operator. This allows the optimizer to choose the best plan for each query independently of the other. DISTINCT is not needed since UNION removes duplicate rows from the result.

SELECT
    CSU.*, U.txtFirstName, U.txtLastName 
FROM
    tblCRMShallowUsers CSU (NOLOCK) 
INNER JOIN 
    tblUsers U (NOLOCK) ON CSU.PK_autUserID = U.PK_autUserID 
INNER JOIN 
    tblUserGroupLink UGL (NOLOCK) ON U.PK_autUserID = UGL.FK_lngUsersID 
INNER JOIN 
    tblModuleSecurityLinks MSL (NOLOCK) ON FK_lngModuleID = 28 
INNER JOIN 
    tblGroups G (NOLOCK) ON G.PK_autGroupID = MSL.FK_lngGroupID 
WHERE 
    MSL.lngRights > 0
    AND U.lngStatus > 19
    AND U.ysnAdminFlag = 0
    AND G.lngStatus > 19
    AND G.ysnFrontEndGroup = 0
    AND UGL.FK_lngGroupID = MSL.FK_lngGroupID
UNION
SELECT
    CSU.*, U.txtFirstName, U.txtLastName 
FROM
    tblCRMShallowUsers CSU (NOLOCK) 
INNER JOIN 
    tblUsers U (NOLOCK) ON CSU.PK_autUserID = U.PK_autUserID 
INNER JOIN 
    tblUserGroupLink UGL (NOLOCK) ON U.PK_autUserID = UGL.FK_lngUsersID 
INNER JOIN 
    tblModuleSecurityLinks MSL (NOLOCK) ON FK_lngModuleID = 28 
INNER JOIN 
    tblGroups G (NOLOCK) ON G.PK_autGroupID = MSL.FK_lngGroupID 
WHERE 
    MSL.lngRights > 0
    AND U.lngStatus > 19
    AND U.ysnAdminFlag = 0
    AND G.lngStatus > 19
    AND G.ysnFrontEndGroup = 0
    AND UGL.FK_lngGroupID = 2 
ORDER BY 
    ysnHasAccess DESC, txtLastName, txtFirstName;

On a side note, be aware than NOLOCK and the READ_UNCOMMITTED isolation level may cause rows to be skipped or duplicated during allocation order scans if data are updated while the query is running. Dirty reads should be used only when concurrency is more important than correct results.

0
On

@DanGuzman @flaschenpost I was also trying with CTE approach to divide the joins into 2 seaprate things but it wasn't working and taking the same time as it was(1 minute). Could you please take a look at below query that I tried.

  1. With CTE approach: With UsersUserGroupLink AS( Select U.PK_autUserID, U.txtFirstName, U.txtLastName, U.lngStatus, U.ysnAdminFlag, UGL.FK_lngGroupID FROM tblUsers as U (NOLOCK) INNER JOIN tblUserGroupLink as UGL (NOLOCK) ON U.PK_autUserID = UGL.FK_lngUsersID ), GroupsModuleSecurityLinks AS(Select MSL.lngRights, G.lngStatus, G.ysnFrontEndGroup, MSL.FK_lngGroupID From
    tblModuleSecurityLinks as MSL (NOLOCK) INNER JOIN tblGroups as G (NOLOCK) ON G.PK_autGroupID = MSL.FK_lngGroupID AND MSL.FK_lngModuleID = 28 ) (SELECT DISTINCT CSU.*, UsersUserGroupLink.txtFirstName, UsersUserGroupLink.txtLastName FROM UsersUserGroupLink Inner join
    tblCRMShallowUsers as CSU ON UsersUserGroupLink.PK_autUserID =
    CSU.PK_autUserID Inner join GroupsModuleSecurityLinks ON
    GroupsModuleSecurityLinks.FK_lngGroupID =
    UsersUserGroupLink.FK_lngGroupID or UsersUserGroupLink.FK_lngGroupID = 2 WHERE GroupsModuleSecurityLinks.lngRights > 0 AND UsersUserGroupLink.lngStatus > 19 AND UsersUserGroupLink.ysnAdminFlag = 0 AND GroupsModuleSecurityLinks.lngStatus > 19 AND GroupsModuleSecurityLinks.ysnFrontEndGroup = 0) ORDER BY ysnHasAccess DESC, txtLastName, txtFirstName