简体   繁体   中英

SQL Server 2012 - Improve query performance

I'm looking for a way to improve the following query.

It collects members of organizations that have a membership of any organization in 2013.

I've been able to determine that the sub-query in this query is the real performance killer, but I can't find a way to remove the subquery and keep the resulting table correct.

The query simply collects all "PersonID" and "MemberId" for people that have a membership in this calendar year. BUT, it is possible to have two memberships in one calendar year. If that should happen, then we only want to select the last membership you have in that calendar year: that's what the subquery is for.

A "WorkingYear" is not the same as a calendar year. A workingyear can be an entire year, but it can also run from september 2013 to september 2014, for example. That's why I specify that the workingyear has to start or end in 2013.

This is the query:

SELECT DISTINCT PersonID,
                m.id AS MemberId
FROM   Members AS m
       INNER JOIN WorkingYears AS w
         ON m.WorkingYearID = w.ID
            AND ( YEAR(w.StartDate) = 2013
                   OR YEAR(w.EndDate) = 2013 )
WHERE  m.Id = (SELECT TOP 1 m2.id
               FROM   DBA_Member m2
               WHERE  personid = m.PersonID
                      AND ( ( droppedOut = 'false' )
                             OR ( droppedOut = 'true'
                                  AND ( yeardropout = 2013 ) ) )
               ORDER  BY m.StartDate DESC) 

This query should collect about 50.000 rows for me, so obviously it also executes the sub query at least 50.000 times and I'm looking for a way to avoid this. Does anyone have any ideas that could point me in the right direction?

All fields that are used in JOINS should be indexed correctly. There is also a seperate index on 'droppedOut' (bit), 'yeardropout' (int). I also created an index on both fields at the same time to no avail.

In the execution plan, I see that an "eager spool" is occurring, that takes up 60% of the query time. It has an outputlist of Member.ID, Member.DroppedOut, Member.YearDropout, which are indeed all the fields that I'm using in my subquery. Also, it gets 50.500 rebinds.

Does anyone have any advice?

You only need to do the sub-query once if you use a CTE

WITH subQall AS
(
  select id, personID, 
             ROW_NUMBER() OVER (PARTITION BY personID ORDER BY StartDate DESC) as rnum
  from DBA_Member 
  WHERE (droppedOut='false') OR (droppedOut='true' AND (yeardropout = 2013))
), subQ AS
(
  select id, personID
  from subQall
  where rnum = 1
)
SELECT DISTINCT PersonID, m.id as MemberId
FROM Members AS m
INNER JOIN WorkingYears AS w ON m.WorkingYearID = w.ID
JOIN subQ ON m.ID = subQ.ID and m.personID = subQ.personID
WHERE StartDate BETWEEN '1-1-2013' AND '12-31-2013'

Can you try a join instead of the sub query?

like this

SELECT DISTINCT PersonID, m.id as MemberId
FROM Members AS m

INNER JOIN WorkingYears AS w ON m.WorkingYearID = w.ID
AND (year(w.StartDate) = 2013 OR year(w.EndDate) = 2013)

JOIN (select top 1 m2.id ID from DBA_Member m2 where personid= m.PersonID
       and ((droppedOut='false') OR (droppedOut='true' AND (yeardropout = 2013)))
       order by m.StartDate desc) Member ON m.Id = Member.ID

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM