问题
I'm trying to group data in time periods. Each period is 5 minutes and I'd like to see what was happening every 5 minutes from 08:00 to 18:00.
I have created a table that has all the time periods in that time range. E.g.:
StartTime EndTime IsBusinessHours
08:40:00.0000000 08:45:00.0000000 1
08:45:00.0000000 08:50:00.0000000 1
08:50:00.0000000 08:55:00.0000000 1
08:55:00.0000000 09:00:00.0000000 1
etc.
Select
TimeDimension.[StartTime],
TimeDimension.[EndTime],
activity.[Description],
activity.[StartTime]
From
TimeDimension
Full Outer Join Activity
on (
Convert(varchar,activity.StartTime,108) >= Convert(varchar,TimeDimension.starttime, 108)
And Convert(varchar,activity.StartTime,108) <= Convert(varchar,TimeDimension.endtime, 108)
)
Where
activity.Date = @DateParam
And TimeDimension.isbusinesshours = 1
I expect to have data grouped by 5 minute time periods, but what I get is:
08:20:00.0000000 08:25:00.0000000 Some activity
08:30:00.0000000 08:35:00.0000000 Some activity
08:45:00.0000000 08:50:00.0000000 Three activities in this time period. First
08:45:00.0000000 08:50:00.0000000 Three activities in this time period. Second
08:45:00.0000000 08:50:00.0000000 Three activities in this time period. Third
When what I'd like to see is:
08:20:00.0000000 08:25:00.0000000 Some activity
08:25:00.0000000 08:30:00.0000000 NULL
08:30:00.0000000 08:35:00.0000000 Some activity
08:35:00.0000000 08:40:00.0000000 NULL
08:45:00.0000000 08:50:00.0000000 Three activities in this time period. First
08:45:00.0000000 08:50:00.0000000 Three activities in this time period. Second
08:45:00.0000000 08:50:00.0000000 Three activities in this time period. Third
This means that I'm displaying time periods when some activities took place, rather than all time periods in that range. I have called a table TimeDimension - but I'm not sure whether this is correct. Gut feeling tells me that this is something to do with analysis services.
Thank you
回答1:
Note 1: Doing DATETIME arithmetic using VARCHAR yields poor performance.
Note 2: You have an OUTER JOIN but then a WHERE clause that doesn't account for NULLs.
This is what I'd use...
WITH
FilteredActivity AS
(
SELECT
Description,
DATEADD(DAY, -DATEDIFF(DAY, 0, StartTime), StartTime) AS StartTime
FROM
Activity
WHERE
Date = @DateParam
)
SELECT
TimeDimension.[StartTime],
TimeDimension.[EndTime],
activity.[Description],
activity.[StartTime]
FROM
TimeDimension
LEFT JOIN
FilteredActivity AS [Activity]
ON Activity.StartTime >= TimeDimension.StartTime
AND Activity.StartTime < TimeDimension.EndTime
WHERE
TimeDimension.isbusinesshours = 1
CTE Filtering
- The CTE at the start filters Activity to just one date
- This avoids the condition in your WHERE clause that plays poorly with OUTER JOINs
CTE Formatting
- The CTE also strips the StartTime down to just a TimePart
- The DATEADD/DATEDIFF business is only need if StartTime includes the DATE as well
- If it is already just the time, just use StartTime
Exclusive vs Inclusive EndTime
- I have
< EndTime
rather than<= EndTime
- This assume intervals in the form of
08:00 to 08:05
and08:05 to 08:10
, etc - Having the EndTime as "the first time you don't want to include" can make things easier
- No more rounding Activity.StartTime down to the nearest minute, for example
- And no strange intervals of
08:00 to 08:04
, etc
An alternative to using Exclusive EndTime values is to round your Activity.StartTime values to the nearest minute. Rather than using strings, the folowing does it using DateTime functions...
- DATEADD(minute, DATEDIFF(minute, 0, Activity.StartTime), 0)
回答2:
You say you want to group the results but you are not applying a GROUP
to the query.
However, if you aggregate the results you will lose the distinct information you are also wanting (Description
, StartDate
) unless they match the other records in the group.
As Scorpi0 commented, a sample of the output you want would be useful.
回答3:
You have a filter on the activity table: activity.Date = @DateParam
.
It prevents from getting every row of the TimeDimension table. Put the filter in the join clause and you will see all your datas.
Select
TimeDimension.[StartTime],
TimeDimension.[EndTime],
activity.[Description],
activity.[StartTime]
From
TimeDimension
Full Outer Join Activity
on (
Convert(varchar,activity.StartTime,108) >= Convert(varchar,TimeDimension.starttime, 108)
And Convert(varchar,activity.StartTime,108) <= Convert(varchar,TimeDimension.endtime, 108)
And activity.Date = @DateParam
)
Where TimeDimension.isbusinesshours = 1
Or you can do also:
Select
TimeDimension.[StartTime],
TimeDimension.[EndTime],
activity.[Description],
activity.[StartTime]
From
TimeDimension
Full Outer Join Activity
on (
Convert(varchar,activity.StartTime,108) >= Convert(varchar,TimeDimension.starttime, 108)
And Convert(varchar,activity.StartTime,108) <= Convert(varchar,TimeDimension.endtime, 108)
)
Where TimeDimension.isbusinesshours = 1
And (activity.Date Is Null Or activity.Date = @DateParam)
回答4:
You need to move activity time condition from common Where clause into Join condition, like so:
Select
TimeDimension.[StartTime],
TimeDimension.[EndTime],
activity.[Description],
activity.[StartTime]
From
TimeDimension
Full Outer Join Activity
on (
Convert(varchar,activity.StartTime,108) >= Convert(varchar,TimeDimension.starttime, 108)
And Convert(varchar,activity.StartTime,108) <= Convert(varchar,TimeDimension.endtime, 108)
And activity.Date = @DateParam
)
Where
TimeDimension.isbusinesshours = 1
来源:https://stackoverflow.com/questions/6909844/sql-how-to-group-in-time-periods