I have this piece of code that counts the number of occurrences for a set of events on an hourly basis. I have it set up to calculate the count for all the hours where locate_received_date is 2016-12-01 (Note: locate_received_date data type is DATETIME with YYYY-MM-DD hh:mm:ss format). I entered the date manually to count for the previous day. The code excerpt is below:
SELECT CAST(locate_received_date as date) AS 'ForDate',
DATEPART(hh, locate_received_date) AS 'OnHour',
COUNT (*) AS 'HourCount'
FROM MyTable
WHERE locate_received_date BETWEEN '2016-12-01 00:00:00.000' AND '2016-12-01 23:59:59.999'
GROUP BY CAST(locate_received_date as date), DATEPART(hh, locate_received_date);
Now the table "MyTable" has hundreds of rows worth of data for many different dates. I need to run the count for all distinct dates, not just manually entered specific ones. I was thinking I could nest the following somehow:
DISTINCT CAST(locate_received_date AS DATE)
Or perhaps do this using some kind of a FOR loop?
EDIT:
Here's the script to create the table. There really isn't any other column worth creating. There is no ID or anything in the table. The table is being updated and every time there is an update, locate_received_date is update with the date and time of occurrence. I need to count first based on hours (which my script does), and also based on days.
CREATE Table myTable(
Locate_Received_Date date);
INSERT INTO myTable(Locate_Received_Date) VALUES ('2016-12-01 15:14:07.000');
INSERT INTO myTable(Locate_Received_Date) VALUES ('2016-12-01 15:13:37.000');
INSERT INTO myTable(Locate_Received_Date) VALUES ('2016-12-02 15:13:37.000');
INSERT INTO myTable(Locate_Received_Date) VALUES ('2016-12-01 15:13:07.000');
INSERT INTO myTable(Locate_Received_Date) VALUES ('2016-12-02 15:12:08.000');
INSERT INTO myTable(Locate_Received_Date) VALUES ('2016-12-02 15:12:07.000');
INSERT INTO myTable(Locate_Received_Date) VALUES ('2016-11-28 15:11:37.000');
INSERT INTO myTable(Locate_Received_Date) VALUES ('2016-12-02 15:11:08.000');
INSERT INTO myTable(Locate_Received_Date) VALUES ('2016-12-02 15:11:07.000');
INSERT INTO myTable(Locate_Received_Date) VALUES ('2016-11-28 15:10:37.000');
INSERT INTO myTable(Locate_Received_Date) VALUES ('2016-11-29 15:10:08.000');
Any help will be appreciated. Thanks
The reason it is only counting one day is that the where clause specifies one day. If you remove the where clause and add an order by, I think you will accomplish what you are looking for. Let me know if your desired output differs from what this renders and I can update the query accordingly:
SELECT CAST(locate_received_date as date) AS 'ForDate',
DATEPART(hh, locate_received_date) AS 'OnHour',
COUNT (*) AS 'HourCount'
FROM MyTable
GROUP BY CAST(locate_received_date as date), DATEPART(hh, locate_received_date)
ORDER BY CAST(locate_received_date as date), DATEPART(hh, locate_received_date);
Your locate_received_date is a datetime, not a date, right? If so, then something like this should work:
select count(Locate_Received_Date) from myTable
group by
convert(varchar(10), cast(Locate_Received_Date as date), 112) + '_' +
convert(varchar(2), DATEPART(hh, Locate_Received_Date))
Is it correct that you need to count occurrences for particular day with hour step? If so, then here is synthetic example:
with a as (select convert(datetime,'2016-12-01 00:01:00.000',120) s
union all
select convert(datetime,'2016-12-01 01:02:02.000',120) s
union all
select convert(datetime,'2016-12-01 00:03:03.000',120) s
union all
select convert(datetime,'2016-12-01 03:04:04.000',120) s
union all
select convert(datetime,'2016-12-01 00:05:05.000',120) s
union all
select convert(datetime,'2016-12-01 06:00:00.010',120) s )
,
b as(select convert(varchar(10), s,120) s
, a.s dt
, DATEPART(hh,s)onhour
from a )
select count(*)cnt, s, onhour
from b
group by s, onhour
Applying to your data it would be:
with a as (select convert(varchar(10), locate_received_date,120)s
, locate_received_date
, DATEPART(hh,locate_received_date)onhour
from MyTable
)
select count(*)cnt, s, onhour
from a
group by s, onhour
the main thing you must remember is the MS SQL Server conversion codes according to this: https://msdn.microsoft.com/en-us/library/ms187928.aspx
Related
I am trying to write a query that returns the time taken by an Order from start to completion.
My table looks like below.
Order No. Action DateTime
111 Start 3/23/2018 8:18
111 Complete 3/23/2018 9:18
112 Start 3/24/2018 6:00
112 Complete 3/24/2018 11:10
Now I am trying to calculate the date difference between start and completion of multiple orders and below is my query:
Declare #StartDate VARCHAR(100), #EndDate VARCHAR(100), #Operation VARCHAR(100)
declare #ORDERTable table
(
order varchar(1000)
)
insert into #ORDERTable values ('111')
insert into #ORDERTable values ('112')
Select #Operation='Boiling'
set #EndDate = (SELECT DATE_TIME from PROCESS WHERE ACTION='COMPLETE' AND ORDER in (select order from #ORDERTable) AND OPERATION=#Operation)
---SELECT #EndDate
set #StartDate = (SELECT DATE_TIME from PROCESS WHERE ACTION='START' AND ORDER in (select order from #ORDERTable) AND OPERATION=#Operation)
---SELECT #StartDate
SELECT DATEDIFF(minute, #StartDate, #EndDate) AS Transaction_Time
So, I am able to input multiple orders but I want to get multiple output as well.
And my second question is if I am able to achieve multiple records as output, how am I gonna make sure which datediff is for which Order?
Awaiting for your answers. Thanks in advance.
I am using MSSQL.
You can aggregate by order number and use MAX or MIN with CASE WHEN to get start or end time:
select
order_no,
max(case when action = 'Start' then date_time end) as start_time,
max(case when action = 'Completed' then date_time end) as end_time,
datediff(
minute,
max(case when action = 'Start' then date_time end),
max(case when action = 'Completed' then date_time end)
) as transaction_time
from process
group by order_no
order by order_no;
You can split up your table into two temp tables, cte's, whatever, and then join them together to find the minutes it took to complete
DECLARE #table1 TABLE (OrderNO INT, Action VARCHAR(100), datetime datetime)
INSERT INTO #table1 (OrderNO, Action, datetime)
VALUES
(111 ,'Start' ,'3/23/2018 8:18'),
(111 ,'Complete' ,'3/23/2018 9:18'),
(112 ,'Start' ,'3/24/2018 6:00'),
(112 ,'Complete' ,'3/24/2018 11:10')
;with cte_start AS (
SELECT orderno, Action, datetime
FROM #table1
WHERE Action = 'Start')
, cte_complete AS (
SELECT orderno, Action, datetime
FROM #table1
WHERE Action = 'Complete')
SELECT
start.OrderNO, DATEDIFF(minute, start.datetime, complete.datetime) AS duration
FROM cte_start start
INNER JOIN cte_complete complete
ON start.OrderNO = complete.OrderNO
Why don't you attempt to approach this problem with a set-based solution? After all, that's what a RDBMS is for. With an assumption that you'd have orders that are of interest to you in a table variable like you described, #ORDERTable(Order), it would go something along the lines of:
SELECT DISTINCT
[Order No.]
, DATEDIFF(
minute,
FIRST_VALUE([DateTime]) OVER (PARTITION BY [Order No.] ORDER BY [DateTime] ASC),
FIRST_VALUE([DateTime]) OVER (PARTITION BY [Order No.] ORDER BY [DateTime] DESC)
) AS Transaction_Time
FROM tableName
WHERE [Order No.] IN (SELECT Order FROM #ORDERTable);
This query works if all the values in the Action attribute are either Start or Complete, but also if there are others in between them.
To read up more on the FIRST_VALUE() window function, check out the documentation.
NOTE: works in SQL Server 2012 or newer versions.
I have this piece of code that counts the number of occurrences for a set of events on an hourly basis. Now I need the MAX COUNT and input that in a different table. I am new to SQL and I am having problems with the UPDATE statement coupled with MAX and all that is below. Can anyone help please? Thanks!
SELECT CAST(locate_received_date as date) AS 'ForDate',
DATEPART(hh, locate_received_date) AS 'OnHour',
COUNT (*) AS 'Count'
FROM BELL_DPRA2_locates_fact
WHERE locate_received_date BETWEEN '2016-12-01 00:00:00.000' AND '2016-12-01 23:59:59.999'
GROUP BY CAST(locate_received_date as date), DATEPART(hh, locate_received_date);
This script results is attached.
Edit: This answer worked for me thanks to #agfc. I modified a bit to work for me.
UPDATE MyTable
SET MaxHourlyCount =
(SELECT Max(A.HourCount)
FROM (SELECT CAST(locate_received_date as date) AS 'ForDate',
DATEPART(hh, locate_received_date) AS 'OnHour',
COUNT (*) AS HourCount
FROM BELL_DPRA2_locates_fact
WHERE locate_received_date BETWEEN '2016-12-01 00:00:00.000' AND '2016-12-01 23:59:59.999'
GROUP BY CAST(locate_received_date as date), DATEPART(hh, locate_received_date)) AS A)
-- I'd rename your 'Count' Field to something else not to use the SQL function name.
UPDATE myOtherTable
SET MaxField = SELECT Max(HourCount) FROM (SELECT CAST(locate_received_date as date) AS 'ForDate',
DATEPART(hh, locate_received_date) AS 'OnHour',
COUNT (*) AS HourCount
FROM BELL_DPRA2_locates_fact
WHERE locate_received_date BETWEEN '2016-12-01 00:00:00.000' AND '2016-12-01 23:59:59.999'
GROUP BY CAST(locate_received_date as date), DATEPART(hh, locate_received_date)) As MyMaxResult;
You can use TOP 1 and ORDER BY to return the row with the Max HourCount. You need only join this as a subquery to your table to update or insert it as a new row. If you include the table you are moving this data to, I can update this answer to reflect your table.
SELECT TOP 1
CAST(locate_received_date as date) AS 'ForDate',
DATEPART(hh, locate_received_date) AS 'OnHour',
COUNT (*) AS 'Count'
FROM BELL_DPRA2_locates_fact
WHERE
locate_received_date BETWEEN '2016-12-01 00:00:00.000' AND '2016-12-01 23:59:59.999'
GROUP BY
CAST(locate_received_date as date),
DATEPART(hh, locate_received_date)
ORDER BY Count DESC;
I have a table that looks like this
ID start_dt end_dt
--------------------------
1 1951-12-05 1951-12-21
2 1951-12-19 1951-12-31
3 1957-12-05 1957-12-19
4 1995-12-06 1995-12-20
5 1996-06-24 1996-07-08
6 1997-05-12 1997-05-26
7 1997-10-07 1997-10-21
8 1997-12-25 1998-01-08
9 1998-01-19 1998-02-02
10 1998-08-05 1998-08-19
I'd like to know how many times each individual date is contained between start_dt and end_dt.
From my example, the result set should look something like this
date count
------------------
1951-12-05 1
1951-12-06 1
...
1951-12-19 2
1951-12-20 2
1951-12-21 2
...
1998-08-19 1
What would be the best way to do this?
EDIT: To clarify, I need each date that appears at least once in a date range (between start_dt and end_dt) to get a row in my result set and I want the number of ranges that this date fits in next to it
hope this helps
When you need to turn 2 values (a range) into a series of rows you can use a number table (see Aaron Bertrand's The SQL Server Numbers Table article if you aren't familiar with the idea).
I've used shorter and simpler data but you should get the idea.
declare #dates table (id int not null, start_dt date not null, end_dt date not null)
insert #dates values (1, '20160601', '20160603'),
(2, '20160603', '20160605'),
(3, '20160610', '20160612')
;with cte as (
select
row_number() over (order by so1.object_id) - 1 as n
from
sys.objects so1
cross join sys.objects so2
)
select
dateadd(d, c.n, d.start_dt) as [date],
count(*)
from
#dates d
join cte c on dateadd(d, c.n, d.start_dt) <= d.end_dt
group by
dateadd(d, c.n, d.start_dt)
order by
dateadd(d, c.n, d.start_dt)
If there are no more than a few days (< 80 or so, depending in your sys.objects table) between start_dt and end_dt, you can use this approach (inspired on Rhys').
DECLARE #dates TABLE (id int not null, start_dt date not null, end_dt date not null)
INSERT #dates VALUES
(1, '1951-12-05', '1951-12-21'),
(2, '1951-12-19', '1951-12-31'),
(3, '1957-12-05', '1957-12-19'),
(4, '1995-12-06', '1995-12-20'),
(5, '1996-06-24', '1996-07-08'),
(6, '1997-05-12', '1997-05-26'),
(7, '1997-10-07', '1997-10-21'),
(8, '1997-12-25', '1998-01-08'),
(9, '1998-01-19', '1998-02-02'),
(10, '1998-08-05', '1998-08-19');
WITH RawData AS (
SELECT
DATEADD(d, n.n, d.start_dt) AS [date]
FROM #dates d
INNER JOIN (
SELECT ROW_NUMBER() OVER (ORDER BY object_id) - 1 AS n FROM sys.objects
) n ON DATEADD(d, n.n, d.start_dt) <= d.end_dt
)
SELECT [date], COUNT(*) [count]
FROM RawData
GROUP BY [date]
ORDER BY [date]
I don't think this could take long even with 1000 date ranges. Perhaps you are using a table with more fields and even missing some index?
You could use a CTE
WITH CTE AS(SELECT start_dt AS dates FROM Table
UNION ALL
SELECT end_dt AS dates FROM Table)
SELECT CAST(dates as DATE) as Date, COUNT(dates) AS Count
FROM CTE c
GROUP BY c.dates
order by Count desc
Or perhaps you need something broader if your columns are of DATETIME data type. This way will GROUP BY the whole day:
WITH CTE AS(SELECT CAST(start_dt AS DATE) AS dates FROM Table
UNION ALL
SELECT CAST(end_dt AS DATE) AS dates FROM Table)
SELECT Dates as Date, COUNT(Dates) AS Count
FROM CTE c
GROUP BY c.dates
order by Count desc
I have a table with two fields - datetime and int. I want to do a group by on the datetime only on the date ignoring the hour and minute. The SELECT statement should return a date that maps to the sum of the int of a single day.
SELECT CAST(Datetimefield AS DATE) as DateField, SUM(intfield) as SumField
FROM MyTable
GROUP BY CAST(Datetimefield AS DATE)
As he didn't specify which version of SQL server he uses (date type isn't available in 2005), one could also use
SELECT CONVERT(VARCHAR(10),date_column,112),SUM(num_col) AS summed
FROM table_name
GROUP BY CONVERT(VARCHAR(10),date_column,112)
I came researching the options that I would have to do this, however, I believe the method I use is the simplest:
SELECT COUNT(*),
DATEADD(dd, DATEDIFF(dd, 0, date_field),0) as dtgroup
FROM TABLE
GROUP BY DATEADD(dd, DATEDIFF(dd, 0, date_field),0)
ORDER BY dtgroup ASC;
-- I like this as the data type and the format remains consistent with a date time data type
;with cte as(
select
cast(utcdate as date) UtcDay, DATEPART(hour, utcdate) UtcHour, count(*) as Counts
from dbo.mytable cd
where utcdate between '2014-01-14' and '2014-01-15'
group by
cast(utcdate as date), DATEPART(hour, utcdate)
)
select dateadd(hour, utchour, cast(utcday as datetime)) as UTCDateHour, Counts
from cte
Personally i prefer the format function, allows you to simply change the date part very easily.
declare #format varchar(100) = 'yyyy/MM/dd'
select
format(the_date,#format),
sum(myfield)
from mytable
group by format(the_date,#format)
order by format(the_date,#format) desc;
I have a query where i have a date column (time) which tells about "IN" & "OUT" timing of the people attendance by this single column
My queries are :-
1) How to get the daily attendance of each employee
2) How to come to know if the employee is present less than 5 hours
Please let me know the queries in SQL server.
You'll need to group the query by the user and the items for a particular day then compare the maximum and minimum values, e.g.
declare #users table (
UserId int,
DateColumn datetime
)
insert into #users values (1, '2008-10-31 15:15')
insert into #users values (1, '2008-10-31 10:30')
insert into #users values (1, '2008-10-30 16:15')
insert into #users values (1, '2008-10-30 10:30')
select
UserID
, cast(dt as datetime) dt
, [in]
, [out]
, case when datepart(hour, [out]-[in]) >= 5 then 'yes' else 'no' end [5Hours?],
, cast(datediff(minute, [in], [out]) as float)/60 [hours]
from (
select
UserID
, convert(varchar, DateColumn, 112) dt
, min(DateColumn) [in]
, max(DateColumn) [out]
from #users
group by
UserID, convert(varchar, DateColumn, 112)
) a
To find the difference between two datetimes you can use the following:
SELECT Datepart(hour, dateTimeEnd - dateTimeStart)
The DatePart function returns part of a date time variable, and the dateTimeEnd - dateTimeStart returns the difference between two dates as a new DateTime
select
datediff(minute, TimeFrom, TimeTo) as AttendedTimeInMinutes,
case when datediff(minute, sTimeFrom, sTimeTo) < 5 * 60
then
'less than 5 hours'
else '5 hours or more'
end
from YourTable