Consider a table entitled 'Calls', that among other things, contains the following columns:
CalledNumber | CallBegin
-----------------------------------
004401151234567|10/08/2013 09:06:53
004303111238493|15/09/2013 14:56:29
and so on...
How would one go about picking out the hour that, averaged over a date range, holds the highest number of rows? The goal is to find the 'Busy Hour' for each quarter in a year. SQL really isn't my forte here, and I'm looking at manipulating up to a 9.6Million rows at times, although, execution time isn't a key problem.
DATEPART is what you're looking for:
SELECT
DATEPART(hour, CallBegin), COUNT(*) as NumberOfCalls
FROM
Test
WHERE
CallBegin BETWEEN '2013-01-01' AND '2013-12-31'
GROUP BY
DATEPART(hour, CallBegin)
ORDER BY
NumberOfCalls DESC
Working DEMO
Use DATEPART. I.e.,
SELECT DATEPART(hh, CallBegin) ,
COUNT(1) as callCount
FROM Calls
GROUP BY DATEPART(hh, CallBegin)
ORDER BY callCount DESC
You can use DATEPART() and CONVERT(), I'm assuming CallBegin is not stored as datetime:
SELECT YEAR(CONVERT(DATETIME,CallBegin,103))'Year'
, DATEPART(quarter,CONVERT(DATETIME,CallBegin,103))'Quarter'
, DATEPART(hour,CONVERT(DATETIME,CallBegin,103))'Hour'
, COUNT(*)
FROM Calls
GROUP BY YEAR(CONVERT(DATETIME,CallBegin,103))
, DATEPART(quarter,CONVERT(DATETIME,CallBegin,103))
, DATEPART(hour,CONVERT(DATETIME,CallBegin,103))
If you want to span years just eliminate the year portion:
SELECT DATEPART(quarter,CONVERT(DATETIME,CallBegin,103))'Quarter'
, DATEPART(hour,CONVERT(DATETIME,CallBegin,103))'Hour'
, COUNT(*)
FROM Calls
GROUP BY DATEPART(quarter,CONVERT(DATETIME,CallBegin,103))
, DATEPART(hour,CONVERT(DATETIME,CallBegin,103))
Rather than datePart, try using DateAdd() and DateDiff()
Select top 1 DateAdd(hour, DateDiff(hour, 0, CallBegin), 0)
From Calls
Group By DateAdd(hour, DateDiff(hour, 0, CallBegin), 0)
Order By Count(*) Desc
This will perform better because it is purely arithmetic, i.e., there is no parsing of textual representations of dates and/or dateTimes.
Add the count to see how many occurred during that hour
Select top 1 DateAdd(hour, DateDiff(hour, 0, CallBegin), 0), Count(*)
From Calls
Group By DateAdd(hour, DateDiff(hour, 0, CallBegin), 0)
Order By Count(*) Desc
Related
I have a table that sends out messages, I would like to get a total count of the messages that have been going out month by month over the last year . I am new to SQL so I am having trouble with it . I am using MSSQL 2012 this is my sql
SELECT sentDateTime, MessageID, status AS total, CONVERT(NVARCHAR(10), sentDateTime, 120) AS Month
FROM MessageTable
WHERE CAST(sentDateTime AS DATE) > '2017-04-01'
GROUP BY CONVERT(NVARCHAR(10), sentDateTime, 120), sentDateTime, MessageID, status
ORDER BY Month;
I think the month() and year() functions are more convenient than datepart() for this purpose.
I would go for:
select year(sentDateTime) as yr, month(sentDateTime) as mon, count(*)
from MessageTable
where sentDateTime > '2017-04-01'
group by year(sentDateTime), month(sentDateTime)
order by min(sentDateTime);
Additional notes:
Only include the columns in the select that you care about. This would be the ones that define the month and the count.
Only include the columns in the group by that you care about. Every combination of the expressions in the group by found in the data define a column.
There is no need to convert sentDateTime to a date explicitly for the comparison.
The order by orders the results by time. Using the min() is a nice convenience.
Including the year() makes sure you don't make a mistake -- say by including data from 2018-04 with 2017-04.
-- this selects the part of the date you are looking for, replace this with the date format you are using, this should give you what you are looking for
SELECT DATEPART(mm, GETDATE())
SELECT COUNT(DATEPART(mm, sentDateTime)), MessageID, status
From MessageTable where Cast(sentDateTime as date) > '2017-04-01'
group by DATEPART(mm, sentDateTime), MessageID, status
order by DATEPART(mm, sentDateTime)
You can group by the month number of the sentDateTime with the function DATEPART(MONTH, sentDateTime). The next select will also yield results if no message was sent for a particular month (total = 0).
;WITH PossibleMonths AS
(
SELECT
M.PossibleMonth
FROM
(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) M(PossibleMonth)
),
MonthTotals AS
(
select
COUNT(1) AS Total,
DATEPART(MONTH, sentDateTime) [Month]
From
MessageTable
where
Cast(sentDateTime as date) > '2017-04-01'
group by
DATEPART(MONTH, sentDateTime)
)
SELECT
P.PossibleMonth,
Total = ISNULL(T.Total, 0)
FROM
PossibleMonths AS P
LEFT JOIN MonthTotals AS T ON P.PossibleMonth = T.Month
Using SQL Server 2014. I have a list of records that are time stamped and I would like to count how many records there are per hour then count how many hours each day that the record count exceeded a given number, say 20.
Here's what I have so far:
select count(distinct datepart(hour, convert(datetime, OrderStateDate))) Count_HoursOver,
datepart(YEAR, convert(datetime, OrderStateDate)) Date_YEAR,
datepart(month, convert(datetime, OrderStateDate)) Date_MONTH,
datepart(day, convert(datetime, OrderStateDate)) Date_DAY
from Reporting.dbo.Orders
group by datepart(YEAR, convert(datetime, OrderStateDate)),
datepart(month, convert(datetime, OrderStateDate)),
datepart(day, convert(datetime, OrderStateDate))
having count(idscript) >= 20
The results aren't correct and I can't make sense of what's being returned and why. Am I using HAVING incorrectly? Any advice would be appreciated!
You kind of have a 2-part question here
I would like to count how many records there are per hour
You can create a query that returns tuples (RecordsPerHour,HOUR,YEAR,MONTH,DAY) as follows:
SELECT
count(*) as RecordsPerHour,
datepart(hour,convert(datetime,OrderStateDate)) as Date_HOUR,
datepart(year,convert(datetime,OrderStateDate)) as Date_YEAR,
datepart(month,convert(datetime,OrderStateDate)) as Date_MONTH,
datepart(day,convert(datetime,OrderStateDate)) as Date_DAY
FROM Reporting.dbo.Orders
GROUP BY
datepart(year,convert(datetime,OrderStateDate)),
datepart(month,convert(datetime,OrderStateDate)),
datepart(day,convert(datetime,OrderStateDate)),
datepart(hour,convert(datetime,OrderStateDate))
then count how many hours each day that the record count exceeded a given number, say 20
To do this, use the query from the first part of your question in a nested query, using a HAVING clause to filter only hours that contain at least 20 orders.
On the outer query, group by (YEAR,MONTH,DAY) to determine the number of hours in that day with at least 20 orders:
SELECT
count(*) as HoursWithAtLeast20Orders,
Date_YEAR,
Date_MONTH,
Date_DAY
FROM
(SELECT
datepart(hour,convert(datetime,OrderStateDate)) as Date_HOUR,
datepart(year,convert(datetime,OrderStateDate)) as Date_YEAR,
datepart(month,convert(datetime,OrderStateDate)) as Date_MONTH,
datepart(day,convert(datetime,OrderStateDate)) as Date_DAY
FROM Reporting.dbo.Orders
GROUP BY
datepart(year,convert(datetime,OrderStateDate)),
datepart(month,convert(datetime,OrderStateDate)),
datepart(day,convert(datetime,OrderStateDate)),
datepart(hour,convert(datetime,OrderStateDate))
HAVING count(*) >=20) as t
GROUP BY
Date_YEAR,
Date_MONTH,
Date_DAY
First round to hour then round to day
SELECT
count(*) as [hours with 20+ Orders],
dateadd(day, datediff(day,'20000101',dt_hour_rounded),'20000101') as dt_day_rounded
FROM (
SELECT
count(*) as OrdersInHour,
dateadd(hour, datediff(hour,'20000101',OrderStateDate),'20000101') as dt_hour_rounded
FROM Reporting.dbo.Orders
GROUP BY dateadd(hour, datediff(hour,'20000101',OrderStateDate),'20000101')
) t
GROUP BY dateadd(day,datediff(day,'20000101',dt_hour_rounded),'20000101')
WHERE OrdersInHour >= 20
Is there a way to run a query for a specified amount of time, say the last 5 months, and to be able to return how many records were created each month? Here's what my table looks like:
SELECT rID, dateOn FROM claims
SELECT COUNT(rID) AS ClaimsPerMonth,
MONTH(dateOn) AS inMonth,
YEAR(dateOn) AS inYear FROM claims
WHERE dateOn >= DATEADD(month, -5, GETDATE())
GROUP BY MONTH(dateOn), YEAR(dateOn)
ORDER BY inYear, inMonth
In this query the WHERE dateOn >= DATEADD(month, -5, GETDATE()) ensures that it's for the past 5 months, the GROUP BY MONTH(dateOn) then allows it to count per month.
And to appease the community, here is a SQL Fiddle to prove it.
Unlike the other two answers, this will return all 5 months, even when the count is 0. It will also use an index on the onDate column, if a suitable one exists (the other two answers so far are non-sargeable).
DECLARE #nMonths INT = 5;
;WITH m(m) AS
(
SELECT TOP (#nMonths) DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE())-number, 0)
FROM master.dbo.spt_values WHERE [type] = N'P' ORDER BY number
)
SELECT m.m, num_claims = COUNT(c.rID)
FROM m LEFT OUTER JOIN dbo.claims AS c
ON c.onDate >= m.m AND c.onDate < DATEADD(MONTH, 1, m.m)
GROUP BY m.m
ORDER BY m.m;
You also don't have to use a variable in the TOP clause, but this might make the code more reusable (e.g. you could pass the number of months as a parameter).
SELECT
count(rID) as Cnt,
DatePart(Month, dateOn) as MonthNumber,
Max(DateName(Month, dateOn)) as MonthName
FROM claims
WHERE dateOn >= DateAdd(Month, -5, getdate())
GROUP BY DatePart(Month, dateOn)
I am new to the concept of Partition. I know horizontal partition but can we do partition date - wise?
in my project I want that whenever we enter in new-year, partition should be created. Can anyone explain how to do this? I am working on ERP sw and it has data of past year and I need partition on year wise.(for example APR-2011 to MAR-2012 is a year)
I think what you are looking for is something like this:
DECLARE #referenceDate datetime = '3/1/2011'
SELECT
[sub].[Period] + 1 AS [PeriodNr],
YEAR(DATEADD(YEAR, [sub].[Period], #referenceDate)) AS [PeriodStartedIn],
COUNT(*) AS [NumberOfRecords],
SUM([sub].[Value]) AS [TotalValue]
FROM
(
SELECT
*,
FLOOR(DATEDIFF(MONTH, #referenceDate, [timestamp]) / 12.0) AS [Period]
FROM [erp]
WHERE [timestamp] >= #referenceDate
) AS [sub]
GROUP BY [sub].[Period]
ORDER BY [sub].[Period] ASC
The fiddle is found here.
When you say partitioning I am curious if you mean in a windowed function or just grouping data in general. Let me show you two ways to aggregate data with partitioning it in a self demonstrating example:
declare #Orders table( id int identity, dt date, counts int)
insert into #Orders values ('1-1-12', 2),('1-1-12', 3),('1-18-12', 1),('2-11-12', 5),('3-1-12', 2),('6-1-12', 8),('10-1-12', 2),('1-13-13', 8)
-- To do days I need to do a group by
select
dt as DayDate
, SUM(counts) as sums
from #Orders
group by dt
-- To do months I need to group differently
select
DATEADD(month, datediff(month, 0, dt), 0) as MonthDate
-- above is a grouping trick basically stating count from 1/1/1900 the number of months of difference from my date field.
--This will always yield the current first day of the month of a date field
, SUM(counts) as sums
from #Orders
group by DATEADD(month, datediff(month, 0, dt), 0)
-- well that is great but what if I want to group different ways all at once?
-- why windowed functions rock:
select
dt
, counts
, SUM(counts) over(partition by DATEADD(year, datediff(year, 0, dt), 0)) as YearPartitioning
, SUM(counts) over(partition by DATEADD(month, datediff(month, 0, dt), 0)) as MonthPartitioning
-- expression above will work for year, month, day, minute, etc. You just need to add it to both the dateadd and datediff functions
, SUM(counts) over(partition by dt) as DayPartitioning
from #Orders
The important concepts on grouping is the traditional group by clause which you MUST LIST that which is NOT performing a math operation on as a pivot to do work on. So in my first select I just chose date and then said sum(counts). It then saw on 1-1-12 it had two values so it added both of them and on everything else it added them individually. On the second select method I perform a trick on the date field to make it transform to the first day of the month. Now this is great but I may want all of this at once.
Windowed functions do groupings inline, meaning they don't need a grouping clause as that is what the over() portion is doing. It however may repeat the values since you are not limiting your dataset. This means that if you look at the third column of the third select 'YearPartitioning' it repeats the number 23 seven times. WHy? Well because you never told the statement to do any grouping outside the function so it is showing every row. The number 23 will occur as long as the expression is true that the year is the same for all values. Just remember this when selecting from a windowed expression.
I have a table with the following fields in an SQL Server 2005 database:
id, integer
value, string
create_date, datetime
New data is constantly being inserted into this table (tens of thousands of records per day) so I use the following query to compare how much data has been inserted on different days.
SELECT CONVERT(varchar(10), create_date, 101) as 'Date', COUNT(*) as 'Record Count',
FROM the_table
GROUP BY CONVERT(varchar(10), create_date, 101)
ORDER BY 'Date' desc
This query returns data looking like this:
12/20/2012 | 48155
12/19/2012 | 87561
12/18/2012 | 71467
However, when running this query today, I noticed the sorting did not work as expected with multiple years worth of data in the database. Instead of the data for this year being at the very top of the result set, it ended up at the bottom (records omitted for clarity)
06/29/2012 | 9987
01/04/2013 | 15768
01/03/2013 | 77586
01/02/2013 | 23566
I understand why this is happening, as my formatted date is simply a string, and sql server can't possibly be expected to sort it as anything but a string. But I would like the ordering to be accurate. How can I achieve this? (the most recent day always appearing first)
Thanks to Oded's suggestion I changed my order by clause and this seems to give me what I want:
SELECT CONVERT(varchar(10), create_date, 101) as 'Date', COUNT(*) as 'Record Count',
FROM the_table
GROUP BY CONVERT(varchar(10), create_date, 101)
ORDER BY MIN(create_date) desc
You can include the date as a date data type in the GROUP BY and then use it in the ORDER BY
SELECT top 100 CONVERT(varchar, create_date, 101) as 'Date', COUNT(*) as 'Record Count'
FROM constituent
GROUP BY CONVERT(varchar, create_date, 101), CONVERT(date, create_date)
ORDER BY CONVERT(date, create_date)
You could truncate the date to 12:00am instead of casting to a string:
SELECT dateadd(dd, datediff(dd, 0, create_date), 0) as 'Date'
, COUNT(*) as 'Record Count',
FROM the_table
GROUP BY dateadd(dd, datediff(dd, 0, create_date), 0)
ORDER BY dateadd(dd, datediff(dd, 0, create_date), 0) desc
You can probably substr then order by year desc, then month asc and date asc.
Does the data have to have only the two columns you specified? If not, you could select the date truncated to midnight (as user1948904 suggested) as well as the formatted-date field, and then order by the date field. Then you can ignore the date field in whatever uses the data.
Edited to correct errors in the original query, and to add the formatted-date field to the GROUP BY, which is required.
SELECT DATEADD(DAY, 0, DATEDIFF(DAY, 0, create_date)) AS raw_date,
CONVERT(VARCHAR(10), create_date, 101) AS 'Date',
COUNT(*) AS 'Record Count',
FROM the_table
GROUP BY DATEADD(DAY, 0, DATEDIFF(DAY, 0, create_date)),
CONVERT(VARCHAR(10), create_date, 101)
ORDER BY raw_date DESC
I find the other answers unsuitable for my situation because I don't want an additional redundant date column or have to use a GROUP BY if I'm not really aggregating any information in the query (granted the OP's question includes count(*) - my case is identical except I'm not aggregating).
This solution uses a DATEADD() that doesn't really do anything to force SQL Server to treat it as an actual date and return the right order.
SELECT [Date] = CONVERT(varchar(10), t.[create_date], 101)
[Thing] = t.[other_column] -- that I don't want to aggregate
FROM [db].[dbo].[mytable] t
ORDER BY DATEADD(dd, 0, t.[create_date]) DESC
I don't know anything about sql-server but I'll try to help. You should replace this column with one that is a Date type. I'm sure sql-server will know how to sort that correctly.
If that isn't an option for you, maybe in sql-server you can order by a function that converts the string to a date type.
But it already looks like you're using a date type here. I think you should just expand your query to include the date column in the select as the date type and sort by that column instead of the converted column.