Reputation: 105
I have a large but slim table that records time spent on activities.
Two tables exist Activities and RecordedTime. Recorded Time holds a date stamp signifying the day the time was spent.
I have a need to get a list of activities that only have time recorded against them in a date range.
Currently I have code which builds an exclusion list and stores those activities into a temporary table:
DECLARE @DontInclude TABLE (ActivityID INT)
INSERT INTO @DontInclude
SELECT DISTINCT ActivityID
FROM RecordedTime
WHERE DateStamp < @StartDate
INSERT INTO @DontInclude
SELECT DISTINCT ActivityID
FROM RecordedTime
WHERE DateStamp > @EndDate
The trouble with this is that alot of data lies outside of small date ranges and therefore a long time.
I cant use BETWEEN as it doesn't bring back activities that have ONLY had time recorded within the specific date range.
I've reviewed the Estimate Execution Plan and created any indexes SQL suggested.
This portion of my SP is still the bottleneck. Can any suggest what other changes I can to improve performance?
Upvotes: 0
Views: 47
Reputation: 1170
How about first gathering a list of ones in the range, then removing the ones that should be excluded:
SELECT DISTINCT tmpId = r.ActivityID
INTO #tmp
FROM RecordedTime r
WHERE r.DateStamp >= @StartDate and r.DateStamp < @EndDate
DELETE FROM #tmp
WHERE exists(select 1 from RecordedTime r
where r.ActivityID = tmpID
and (r.DateStamp < @startDate or
r.DateStamp > @endDate))
This should be quicker since you only check exclusion conditions ("not exists") on the ones that might be included; rather than running "not exists" on everything in the table.
Upvotes: 0
Reputation: 13723
You could combine the insert statement into one query really to make it more efficient like this:
DECLARE @DontInclude TABLE (ActivityID INT)
INSERT INTO @DontInclude
SELECT DISTINCT ActivityID
FROM RecordedTime
WHERE DateStamp < @StartDate OR Datestamp > @EndDate
Ofcourse, like @Gordon Linoff mentions, adding a non-clustered index on your recordedtime
table would make it quite faster!
Upvotes: 0
Reputation: 1269623
The query that you want sounds like this:
select a.*
from activities a
where not exists (select 1
from RecordedTime rt
where rt.activityId = a.activityId and
dateStamp < @StartDate
) and
not exists (select 1
from RecordedTime rt
where rt.activityId = a.activityId and
dateStamp > @EndDate
) and
exists (select 1
from RecordedTime rt
where rt.activityId = a.activityId
);
For performance, you want an index on RecordedTime(activityId, datestamp)
.
Note that the use of three subqueries is quite intentional. Each subquery should make optimal use of the indexes, so the query should be fairly fast.
Upvotes: 2