Extract quarters from time - sql

Is is possible to breakdown hour into quarters and extract them?
For example:
7:00-7:15 = 1
7:15-7:30 = 2
etc
I have time column with values such as 09:30
and I want to extract:
hour = 6
quarter = 2
I can take out the hour, but how do I take out the quarter.

Use the datepart function:
select
case
when TimeColumn is null then null
when datepart(minute, TimeColumn) < 15 then 1
when datepart(minute, TimeColumn) < 30 then 2
when datepart(minute, TimeColumn) < 45 then 3
else 4
end
from MyTable

Related

Extract the record for last hour for specific date

I am trying to extract the last hour (TKT_DT) record for number of tickets (TKT_DN) from sales table (PS_TKT_HIST) for specific date (BUS_DAT).
I have the following code but it extracts the number of tickets (TKT_NO) for each hour. I want to filter the last hour only. Here is the code I used:
Select count(TKT_NO) AS SAL_TKTS,
DATEPART(HOUR, (TKT_DT))AS SAL_HR
FROM PS_TKT_HIST
WHERE BUS_DAT = '2022-03-30'
GROUP By DATEPART (HOUR, TKT_DT)
I get the flowing results
SAL_TKTS SAL_HR
5 10
1 11
3 12
5 13
10 14
13 15
23 16
18 17
12 18
6 19
6 20
4 21
I want to get only the record (4) for the last hour (21)
If you just want the number of tickets in the last hour on a given day:
DECLARE #date date = '20220330';
SELECT COUNT(*)
FROM dbo.PS_TKT_HIST
WHERE BUS_DAT = #date
AND TKT_DAT >= DATEADD(HOUR, 23, CONVERT(datetime2, #date));
For any hour other than the last hour (let's say, the 9PM hour):
WHERE BUS_DAT = #date
AND TKT_DAT >= DATEADD(HOUR, 21, CONVERT(datetime2, #date))
AND TKT_DAT < DATEADD(HOUR, 22, CONVERT(datetime2, #date));
If by "last hour" you don't mean 11 PM but rather the last hour there was a sale, you would have to do something like this:
DECLARE #date date = '20220330';
SELECT TOP (1) COUNT(*)
FROM dbo.PS_TKT_HIST
WHERE BUS_DAT = #date
GROUP BY DATEPART(HOUR, TKT_DAT)
ORDER BY DATEPART(HOUR, TKT_DAT) DESC;

Averages are too high when getting data from over a month

I was asked to alter a query to work with data from a given date selection instead of just the current month. The query should get the average sales per hour during that date range. It appears to work just fine when selecting one month of data, but when I try go to over a month, the averages appear to be higher than they ought to.
I think the problem may have to do with grouping by the day, since the day would be doubled up when data is over a month, but how would I go about fixing it? Thanks in advance.
DECLARE #Start DATETIME
DECLARE #End DATETIME
SET #Start = '6/15/2015'
SET #End = '8/15/2015'
SELECT TheHour, AVG(TheCount) AS SalesPerHour
FROM
(SELECT DATEPART(DAY, DateTimeCreated) AS TheDay,
DATEPART(HOUR, DateTimeCreated) AS TheHour,
COUNT(*) AS TheCount
FROM OrderHeader
WHERE Deleted = 0
AND OrderType = 1
AND BranchID = 4
AND BackOrderedFromID IS NULL
AND DateTimeCreated >= #Start
AND DateTimeCreated < #End
GROUP BY DATEPART(DAY, DateTimeCreated), DATEPART(HOUR, DateTimeCreated)) AS T
GROUP BY TheHour
ORDER BY TheHour
SAMPLE DATA for 6/15/2015 to 7/15/2015
TheHour SalesPerHour
5 2
6 5
7 6
8 5
9 4
10 4
11 2
12 2
13 3
14 2
15 2
16 1
SAMPLE DATA for 7/15/2015 to 8/15/2015
TheHour SalesPerHour
5 1
6 7
7 6
8 5
9 4
10 4
11 4
12 2
13 4
14 2
15 1
SAMPLE DATA for 6/15/2015 to 8/15/2015 (most values are too high?)
TheHour SalesPerHour
5 2
6 10
7 11
8 8
9 7
10 6
11 5
12 3
13 5
14 4
15 2
16 1
Don't use datepart(day). This gives the day of the month. When your time frame spans multiple months, datepart(day) returns the same value for different days (for instance, "1" on the first of any month).
Instead, simply cast the value to a date to remove the time component. The rest of the query remains the same:
SELECT TheHour, AVG(TheCount) AS SalesPerHour
FROM (SELECT CAST(DateTimeCreated as Date) AS TheDay,
DATEPART(HOUR, DateTimeCreated) AS TheHour,
COUNT(*) AS TheCount
FROM OrderHeader
WHERE Deleted = 0 AND OrderType = 1 AND BranchID = 4 AND
BackOrderedFromID IS NULL AND
DateTimeCreated >= #Start
DateTimeCreated < #End
GROUP BY CAST(DateTimeCreated as Date), DATEPART(HOUR, DateTimeCreated)
) dh
GROUP BY TheHour
ORDER BY TheHour;
Alternatively, you can do this without the double aggregation:
SELECT DATEPART(HOUR, DateTimeCreated) as TheHour,
(COUNT(*) * 1.0 /
COUNT(DISTINCT CAST(DateTimeCreated as Date))
) as SalesPerHour
FROM OrderHeader oh
WHERE Deleted = 0 AND OrderType = 1 AND BranchID = 4 AND
BackOrderedFromID IS NULL AND
DateTimeCreated >= #Start
DateTimeCreated < #End
GROUP BY DATEPART(HOUR, DateTimeCreated);
Also, note that AVG() of an integer value does an integer average. So, the average of 1 and 2 is 1 in SQL Server, not 1.5. In this version the query multiplies the count by 1.0 to get decimal places -- that may or may not be desirable.
To round a datetime down to it's nearest whole hour, use DATEADD and DATEDIFF together:
DECLARE #Start DATETIME
DECLARE #End DATETIME
SET #Start = '6/15/2015'
SET #End = '8/15/2015'
SELECT DATEPART(hour,RoundedHour) as Hour, AVG(TheCount) AS SalesPerHour
FROM
(SELECT DATEADD(hour,DATEDIFF(hour,0,DateTimeCreated),0) as RoundedHour,
COUNT(*) AS TheCount
FROM OrderHeader
WHERE Deleted = 0
AND OrderType = 1
AND BranchID = 4
AND BackOrderedFromID IS NULL
AND DateTimeCreated >= #Start
AND DateTimeCreated < #End
GROUP BY DATEADD(hour,DATEDIFF(hour,0,DateTimeCreated),0)) AS T
GROUP BY DATEPART(hour,RoundedHour)
ORDER BY DATEPART(hour,RoundedHour)
That way you don't have to think about all of the larger components (day, month, year) that you'd also want to group by, for larger ranges.
Since your query is using DAY as the datepart, you're effectively adding the number of sales in each hour on each day before getting the averages. For example, if a salesperson has 10 sales in the 5pm hour on Jan. 1st and 12 sales on Feb. 1st in the 5pm hour then you're going to get an intermediate value of 22 sales for "day 1". You end up averaging these over the days of each individual month, but then not over the days themselves.
You could use the DATEPART of DY (day of year) instead, but then your query would experience the same issue if you started to span years. Instead, just CAST the DATETIME as a DATE to get rid of the time portion, or even better yet, use a windowed function to get your numbers, like so:
;WITH CTE_HourBreakdown AS
(
SELECT
DATEPART(HOUR, DateTimeCreated) AS hr,
COUNT(*) OVER (PARTITION BY (YEAR(DateTimeCreated), DATEPART(DY, DateTimeCreated), DATEPART(HOUR, DateTimeCreated)) AS cnt
FROM
OrderHeader
)
SELECT
hr,
AVG(CAST(cnt AS DECIMAL(10, 2)))
FROM
CTE_HourBreakdown
GROUP BY
hr
There's likely a better way to do this with windowed functions, but this was the first thing that came to me. Also, note that if there are no sales in an hour this method does NOT average that into the results. For example, if on one day between 4pm and 5pm there are no sales and the next day there are 2 sales this will show an average of 2 sales between 4pm and 5pm instead of 1 sale on average. If you want to account for that then you'll need a method to distinguish zero-sale hours from hours when no one is working.

SQL count number of users every 7 days

I am new to SQL and I need to find count of users every 7 days. I have a table with users for every single day starting from April 2015 up until now:
...
2015-05-16 00:00
2015-05-16 00:00
2015-05-17 00:00
2015-05-17 00:00
2015-05-17 00:00
2015-05-17 00:00
2015-05-17 00:00
2015-05-18 00:00
2015-05-18 00:00
...
and I need to count the number of users every 7 days (weekly) so I have data weekly.
SELECT COUNT(user_id), Activity_Date FROM TABLE_NAME
I need output like this:
TotalUsers week1 week2 week3 ..........and so on
82 80 14 16
I am using DB Visualizer to query Oracle database.
You should try following,
Select
sum(Week1) + sum(Week2) + sum(Week3) + sum(Week4) + sum(Week5) as Total,
sum(Week1) as Week1,
sum(Week2) as Week2,
sum(Week3) as Week3,
sum(Week4) as Week4,
sum(Week5) as Week5
From (
select
case when week = 1 then 1 else 0 end as Week1,
case when week = 2 then 1 else 0 end as Week2,
case when week = 3 then 1 else 0 end as Week3,
case when week = 4 then 1 else 0 end as Week4,
case when week = 5 then 1 else 0 end as Week5
from
(
Select
CEILING(datepart(dd,visitdate)/7+1) week,
user_id
from visitor
)T
)D
Here is Fiddle
You need to add month & year in the result as well.
SELECT COUNT(user_id), Activity_Date FROM TABLE_NAME WHERE Activity_Date > '2015-06-31';
That would get the amount of users for the last 7 days.
This is my test table:
user_id act_date
1 01/04/2015
2 01/04/2015
3 04/04/2015
4 05/04/2015
..
This is my query:
select week_offset, count(*) nb from (
select trunc((act_date-to_date('01042015','DDMMYYYY'))/7) as week_offset from test_date)
group by week_offset
order by 1
and this is the output:
week_offset nb
0 6
1 3
4 5
5 7
6 3
7 1
18 1
Week offset is the number of the week from 01/04/2015, and we can show the first day of the week.
See here for live testing.
How do you define your weeks? Here's an approach for SQL Server that starts each seven-day block relative to the start of April. The expressions will vary according to your specific needs:
select
dateadd(
dd,
datediff(dd, cast('20150401' as date), Activity_Date) / 7 * 7,
cast('20150401' as date)
) as WeekStart,
count(*)
from T
group by datediff(dd, cast('20150401' as date), Activity_Date) / 7
Oracle:
select
trunc(Activity_date, 'DAY') as WeekStart,
count(*)
from T
group by trunc(Activity_date, 'DAY') /* D and DAY are the same thing */

SQL Date Range Queries

I am working with a TABLE with date ranges bound by two fields (Start and End):
ID | START | END
1 2010-01-01 2010-07-01
2 2011-01-01 2011-07-01
3 2012-01-01 2013-07-01
4 2013-01-01 2013-07-01
5 2009-01-01 2013-07-01
I could get a rough estimate of the number of 'months' represented by all ranges by a query like:
SELECT SUM(DATEDIFF(dy, Start, End) / 30) as Total_Months
FROM TABLE
What I would rather like to do is to query how many months (or days) are represented by all ranges within a given period of time.
So If I asked how many months are represented from the time period [2013-01-01 - 2013-07-01] in the example above, it would say 18 (6 from each of rows 3, 4 and 5).
What is the best way to accomplish this?
UPDATED: You can do it like this
SELECT SUM(DATEDIFF(dy,
CASE WHEN '2013-01-01' > Start THEN '2013-01-01' ELSE Start END,
CASE WHEN '2013-07-01' < [End] THEN '2013-07-01' ELSE [End] END) / 30) Total_Months
FROM Table1
WHERE '2013-01-01' BETWEEN Start AND [End]
AND '2013-07-01' BETWEEN Start AND [End]
Output:
| TOTAL_MONTHS |
----------------
| 18 |
Here is SQLFiddle demo
Something like this?
SELECT SUM(DATEDIFF(dy, Start, End) / 30) as Total_Months
WHERE Start >= '2013-01-01' AND End <= '2013-07-01'
FROM TABLE
Or to make it parameterized:
SELECT SUM(DATEDIFF(dy, Start, End) / 30) as Total_Months
WHERE Start >= #startDate AND End <= #endDate
FROM TABLE

T-SQL Question - Counting and Average

I have a set of data that consists of a filenbr, open date and close date.
I need to produce a summary table similar to the below, i need to count how many files belong to each day period, but i need those greater than 20 grouped together. I know how to get the datediff, what i'm stumbling on is how to get the 20+ and the % column
1 day - 30 files - 30%
3 days - 25 files - 25%
10 days - 5 files - 5%
13 days - 20 files - 20%
>= 20 days - 20 files - 20%
suppose you have a table named dayFile with the following columns
Table DayFile
days - files
1 - 10
1 - 5
1 - 15
3 - 20
3 - 5
10 - 5
13 - 20
20 - 5
22 - 5
28 - 10
Then you could do the following
SELECT
SummaryTable.Day,
SUM(SummaryTable.Files) as SumFiles,
Sum(SummaryTable.Files) / SummaryTable.TotalFiles
FROM
(SELECT
CASE WHEN (days >= 20) THEN
20
ELSE DF.days END as Day
files,
(SELECT Count(*) FROM DayFile DFCount) as TotalFiles
FROM DayFile DF) SummaryTable
Group By SummaryTable.Day
EDITED:
SELECT
SummaryTable.Day,
SUM(SummaryTable.Files) as SumFiles,
Sum(SummaryTable.Files) / (SELECT Count(*) FROM DayFile DFCount)
FROM
(SELECT
CASE WHEN (days >= 20) THEN
20
ELSE DF.days END as Day
files
FROM DayFile DF) SummaryTable
Group By SummaryTable.Day
You are unclear as to how the ranges are determined (e.g. does "3 days mean < 3 days, <= 3 days, = 3 days, > 3 days or >= 3 days?). If you are using SQL Server 2005 and higher, you get your results like so:
With PeriodLength As
(
Select DateDiff(d, OpenDate, CloseDate) As DiffDays
From Table
)
, Ranges As
(
Select Case
When DiffDays < 3 Then 'Less than 3 Days'
When DiffDays >= 3 And DiffDays < 10 Then 'Less than 10 Days'
When DiffDays >= 10 And DiffDays < 13 Then 'Less than 13 Days'
When DiffDays >= 13 And DiffDays < 20 Then 'Less than 20 Days'
When DiffDays >= 20 Then 'Greater than 20 days'
End As Range
From PeriodLength
)
Select Range
, Count(*) As FileCount
, Count(*) * 100.000 / (Select Count(*) From Ranges) As Percentage
From Ranges
Group By Range