Reputation: 635
I have to sort, by hour, the number of times someone calls from a specific country. The list of countries increases on a monthly basis, so for example we can add Brazil. I am using SQL Server.
The Data Looks like This
2012-04-02 08:00:59 United States
2012-04-02 08:12:02 United States
2012-04-02 08:13:42 Canada
2012-04-02 08:13:56 United States
2012-04-02 08:14:07 Mexico
2012-04-02 08:18:09 Canada
2012-04-02 08:19:50 United States
2012-04-02 08:34:34 Mexico
etc.
How I would like to list the data is by top 2 countries by hour.
I would like it to display like:
Date Country Calls
2012-04-02 08:00:00 United States 24
2012-04-02 08:00:00 Canada 19
--hidden--
2012-04-02 08:00:00 Mexico 12
The Code that i tried (does not work):
Declare @StartDate datetime, @EndDate datetime
set @StartDate = '20120401 00:00:00'
set @EndDate = '20120430 23:59:59'
SELECT DATEADD(HOUR, DATEPART(HOUR, [date]), DATEDIFF(DAY, 0, [date])) as [date],
(SELECT COUNT([country]) FROM [mytable] WHERE [date] between @StartDate and @EndDate and [country] = 'United States' ) as [United_States]
,(SELECT COUNT([country]) FROM [mytable] WHERE [date] between @StartDate and @EndDate and [country] = 'Canada' ) as [Canada]
,(SELECT COUNT([country]) FROM [mytable] WHERE [date] between @StartDate and @EndDate and [country] = 'Mexico' ) as [Canada]
FROM [mytable]
WHERE [date] between @StartDate and @EndDate
GROUP BY DATEADD(HOUR, DATEPART(HOUR, [date]), DATEDIFF(DAY, 0, [date]))
ORDER BY [date]
Thank You.
Upvotes: 4
Views: 239
Reputation: 3902
Try this. Maybe there are some syntax errores, but I think it goes on the right direction.
SELECT T1.Date, T1.Country, SUM(1)
FROM CallsTable T1
GROUP BY DATEADD(hh, DATEDIFF(hh,0,T1.Date),0), T1.Country
HAVING T1.Country IN (SELECT TOP 2 T2.Country
FROM CallsTable T2
WHERE DATEADD(hh, DATEDIFF(hh,0,T2.Date),0) = DATEADD(hh, DATEDIFF(hh,0,T1.Date),0)
GROUP BY T2.Country
ORDER BY SUM(1) DESC
)
Explanation:
DATEADD(hh, DATEDIFF(hh,0,T1.Date),0)
truncates minutes and seconds, in order to group by hour.
The main select groups by hour and countries. The inner select is a filter so only the two top calling country of an specified hour are shown.
Upvotes: 1
Reputation: 460108
Using a cte and over
clause it's relatively easy and readable:
WITH CTE AS(
SELECT DATEADD(HOUR, DATEDIFF(HOUR, 0, Date), 0)AS DateHour
, Country
, Count(*) OVER(Partition By Country, DATEPART(HOUR, Date))as GrpCount
FROM @mytable
WHERE date between @StartDate and @EndDate
)
SELECT DateHour AS Date, Country, GrpCount AS Calls
FROM CTE
GROUP BY Country, DateHour, GrpCount
ORDER BY DateHour
Here's your sample data:
declare @myTable table(date datetime, country varchar(20));
insert into @myTable values(convert(datetime,'2012-04-02 08:00:59',102),'United States');
insert into @myTable values(convert(datetime,'2012-04-02 08:13:42',102),'Canada');
insert into @myTable values(convert(datetime,'2012-04-02 08:13:56',102),'United States');
insert into @myTable values(convert(datetime,'2012-04-02 08:14:07',102),'Mexico');
insert into @myTable values(convert(datetime,'2012-04-02 08:18:09',102),'Canada');
insert into @myTable values(convert(datetime,'2012-04-02 08:19:50',102),'United States');
insert into @myTable values(convert(datetime,'2012-04-02 08:34:34',102),'Mexico');
Declare @StartDate datetime, @EndDate datetime;
set @StartDate = '20120401 00:00:00';
set @EndDate = '20120430 23:59:59';
Result:
Date Country Calls
2012-04-02 08:00:00.000 Canada 2
2012-04-02 08:00:00.000 Mexico 2
2012-04-02 08:00:00.000 United States 3
Upvotes: 1
Reputation: 56725
This should do it:
Declare @StartDate datetime,
@EndDate datetime
set @StartDate = '20120401 00:00:00'
set @EndDate = '20120430 23:59:59'
;WITH
mytablePlusHours As
(
SELECT *,
DATEADD(HOUR, DATEPART(HOUR, [date]), DATEDIFF(DAY, 0, [date])) AS [dateHour]
FROM [mytable]
)
, mytableHourGroups As
(
SELECT dateHour,
country,
COUNT(*) As [countryCount],
ROW_NUMBER() OVER(PARTITION BY dateHour ORDER BY countryCount)
As [countryRank]
FROM mytablePlusHours
GROUP BY dateHour, country
)
SELECT
dateHour AS [date],
country,
countryCount
FROM mytableHourGroups
WHERE [date] between @StartDate and @EndDate
AND countryRank <= 2
ORDER BY [date], countryRank
Upvotes: 1
Reputation: 35323
Untested:
SELECT date(yourdate) as mday,
datepart(hour, yourdate) as Yourhour,
country,
count(*) as calls,
ROW_NUMBER() OVER(PARTITION BY date(YourDate) & "-" & Datepart(hour,YourDate) & ORDER BY count(*) DESC) myCount
FROM yourTableName
GROUP BY date(yourdate), datepart(hour, yourdate)
having myCount <3
Order by count(*), mday, yourhour
Somehow Row_NUMBER() Over (partiion By... seems like it will get you what you want' but I can't test it. See MSFT ARTICLE for use and why I think it's what your after.
In essence, you want to create different partitions on the day and hour, order by the count of calls within that partition set and assign a number starting at 1 increasing by 1 for each record/count. Then you want to limit that record/count to the top 2 of each day/hour grouping.
Upvotes: 0
Reputation: 594
Adding to Darren Davies answer, you could further define the table output to resolve by date and hour:
SELECT
DATEADD(dd, 0, DATEDIFF(dd, 0, [Date])) as Date,
DATEPART(HOUR, [Date]) as Hour,
Country,
SUM(Calls)
FROM YourTable
GROUP BY
DATEADD(dd, 0, DATEDIFF(dd, 0, [Date])),
DATEPART(HOUR, [Date]),
Country
Upvotes: 0
Reputation: 7692
I believe the simplest solution is in two steps.
First you have to convert the datetimes to even hours. Do it by copying data from one table to another or through a view or adding a calculated field to the table.
Then you do something like
select date time, country, count(*) calls
from mytable
group by date, country
I am not sure that it is "count(*)" - someone please correct me.
Upvotes: 0