Join Table variable with a Table - SQL - sql

This question might looks simple and repeated, Since I am beginner in SQL, I have stuck up with this problem.
I have created a table variable to store hour range in a 24 hr format. Here is the code
DECLARE #TIMERANGE TABLE ([TIME] NVARCHAR(MAX))
;with hrs (time)
AS
(
SELECT 0
UNION ALL
SELECT time+1
FROM hrs WHERE time<23
)
INSERT INTO #TIMERANGE select
RIGHT ('0000' + CONVERT(VARCHAR, time), 4) + '-' + RIGHT('0000' + CONVERT(VARCHAR, time + 1), 4) AS [TIME]
from hrs
output for this table is:
TIME
0000-0001
0001-0002
0002-0003
0003-0004
0004-0005
0005-0006
0006-0007
0007-0008
0008-0009
0009-0010
0010-0011
0011-0012
0012-0013
0013-0014
0014-0015
0015-0016
0016-0017
0017-0018
0018-0019
0019-0020
0020-0021
0021-0022
0022-0023
0023-0024
Condition is, I want to join this with my real table with a specific condition
Id Date Time Score
1 2008-01-01 00:05 15
2 2008-01-01 00:15 20
3 2008-01-02 10:15 05
4 2008-01-02 11.00 55
I want to find the sum of score in specific time range, Eg, 00.15 will falls in Time range 0000-0001.
Desired output is:
Time Range Score
0000-0001 25
........ ..
........ ..
Please Help

I am hoping I understood the requirements. I see why you did the CTE. I've done that to support graphs so every hour in the day is represented, with or without resulting data.
I re factored the query to produce the following:
declare #tmp TABLE (MyDate DATE, MyTime TIME,Score INT)
INSERT INTO #tmp VALUES('2008-01-01','00:05',15),
('2008-01-01','00:15',20),
('2008-01-02','10:15',05),
('2008-01-02','11:00',55)
SELECT SUM(Score) Score,datepart(hour,GETDATE()) TimeRange FROM #tmp Group By datepart(hour,MyTime)
The result will show the SUM (or average or whatever you need) by Hour. If you still need to graph the result THEN join back into your CTE on the Hour component of the time.
Hope this helps.

Related

Subtracting Time Intervals in SQL

I have a table with 2 rows of time intervals. The data is stored as a varchar containing the hour and minutes, like the sample data below. I need to find a way to subtract the first column of data from the second and keep it as a time interval format. As seen below in the following table, I want to subtract colB from colA, to get the numbers in desiredOutcome displayed in the same format.
colA
colB
desiredOutcome
10:30
1:30
9:00
1:50
1:47
0:03
20:10
15:15
4:55
250:10
50:05
200:05
Any and all help is greatly appreciated
EDIT:
To clarify, these are time intervals, not times of the day. I have added more data to the table to show this, it is the amount of time taken in hours and minutes to perform a task. Also, the data is stored in varchar, but I can just use CAST() to change that.
Will say would have recommended storing this as simple minutes using a data type like int, then format for the user to your HH:MM format. It would make calculations like this a lot easier.
So for example instead of storing 250:10, would have stored it as 15010 minutes
Below I converted your HH:MM time lengths to minutes, did the calculation, and then formatted it back again.
Calculating Difference Between HH:MM Time Lengths
DROP TABLE IF EXISTS #Interval
CREATE TABLE #Interval (ID INT IDENTITY(1,1) PRIMARY KEY, colA Varchar(10),colB Varchar(10))
INSERT INTO #Interval
VALUES
('10:30','1:30')
,('1:50','1:47')
,('20:10','15:15')
,('250:10','50:05')
SELECT *
,DesiredOutcome = CONCAT(FLOOR(DeltaInMinutes/60.0),':',FORMAT(DeltaInMinutes % 60,'0#'))
FROM #Interval AS A
/*Find the colons*/
CROSS APPLY (
SELECT idxA = NULLIF(CHARINDEX(':',A.ColA),0)
,idxB = NULLIF(CHARINDEX(':',A.ColB),0)
) AS B
/*Break up into hours and minutes*/
CROSS APPLY(
SELECT HrsA = ISNULL(SUBSTRING(A.ColA,0,idxA),0)
,HrsB = ISNULL(SUBSTRING(A.ColB,0,idxB),0)
,MinutesA = ISNULL(SUBSTRING(A.ColA,idxA+1,100),0)
,MinutesB = ISNULL(SUBSTRING(A.ColB,idxB+1,100),0)
) AS C
/*Find total duration in minutes*/
CROSS APPLY (
SELECT TotalMinutesA = (HrsA * 60) + MinutesA
,TotalMinutesB = (HrsB * 60) + MinutesB
) AS D
/*Calculate delta*/
CROSS APPLY (
SELECT DeltaInMinutes = TotalMinutesA - TotalMinutesB
) AS E

sql query for finding ID numbers on date range

I want to get the ID numbers for the last 24 hour range. Say I run a task at 4:00AM each morning and want to get the previous 24 hours of data going back to 4:00AM the previous day. I need to get the id codes to search the correct tables. If the data is like this what would be the best way to query the ID numbers?
ID
Start Time
EndTime
2112
2021-08-10 23:25:28.750
NULL
2111
2021-08-06 17:42:27.400
2021-08-10 23:25:28.750
2110
2021-08-03 20:21:14.093
2021-08-06 17:42:27.400
So if I had the date range of 8/10 - 8/11 I would need to get two codes. 2111 and 2112. If I need to get 8/11 - 8/12 I would only get 2112 as the endtime is null.
Any thoughts on the best way to query this out?
You need to do something like that :
DECLARE #employee TABLE(
ID int,
StartTime datetime,
EndTime datetime
)
INSERT INTO #employee SELECT '2112','2021-08-10 23:25:28.750',NULL
INSERT INTO #employee SELECT '2111','2021-08-06 17:42:27.400','2021-08-10 23:25:28.750'
INSERT INTO #employee SELECT '2110','2021-08-03 20:21:14.093','2021-08-06 17:42:27.400'
SELECT ID,* from #employee where
EndTime >= GETDATE()-1 OR EndTime is null
It will takes -1 day from execution time . So if you execute it right now you will heave only null value in output - because now it's 14.08 and this Edtime is null ( still running i think ).
DBFiddleDemo

How to get timebased data from MSSQL 2008 aggregated at time interval

I have an equipment that reports its number of produced pieces at random time intervals. At each record, the internal counter is reset, so if I want to get the total pieces, I would net to sum over an interval.
ts pieces
--------------------------------
2013-01-23 11:58 2013
2013-01-23 12:12 3025
2013-01-23 12:12 3025
2013-01-23 12:13 112
2013-01-23 12:17 1122
2013-01-23 12:34 3112
2013-01-23 12:36 3025
What if I want to query this data and I want the produced pieces from 12:00 to 12:30. I cannot simply do the following:
SELECT SUM(pieces)
FROM table
WHERE ts BETWEEN '2013-01-23 12:00' and '2013-01-23 12:30'
With this query, I would have too many pieces in the beginning (as of the 3025 pieces reported at 12:12 some were produced in the 2 minutes before 12:00) and I would have too little pieces at the end (pieces produced between 12:17 and 12:30 were only reported at 12:34).
Is there a built in feature in SQL server to do such calculations on timebased series, or would it require me to manually interpolate based on dateDiff between first/last values in the interval and last/first outside?
You have to do the interpolation manually. I've built some code that does it, based on various other assumptions (i.e. whether ts represents "all items produced up until and including this minute" or "all items produced before this minute"):
declare #t table (ts datetime2 not null,pieces int not null)
insert into #t(ts,pieces) values
('2013-01-23T11:58:00',2013),
('2013-01-23T12:12:00',3025),
--('2013-01-23T12:12:00',3025), --Error, two identical counts at same time?
('2013-01-23T12:13:00',112 ),
('2013-01-23T12:17:00',1122),
('2013-01-23T12:34:00',3112),
('2013-01-23T12:36:00',3025)
declare #Start datetime2
declare #End datetime2
select #Start = '2013-01-23T12:00:00', #End = '2013-01-23T12:30:00'
;With Numbers as (
select distinct number
from master..spt_values
), RowsNumbered as (
select
ROW_NUMBER() OVER (ORDER BY ts) as rn,
*
from #t
), Paired as (
select
rn2.ts as prevTime,
rn1.ts as thisTime,
rn1.pieces as pieces
from
RowsNumbered rn1
inner join
RowsNumbered rn2
on
rn1.rn = rn2.rn + 1
), Minutes as (
select
DATEADD(minute,-number,thisTime) as Time,
(pieces * 1.0) / DATEDIFF(minute,prevTime,thisTime) as Pieces
from
Paired p
inner join
Numbers n
on
number < 60 and --Reasonable?
DATEADD(minute,-number,thisTime) > prevTime and
number >= 0
)
select SUM(pieces)
from Minutes
where Time >= #Start and Time < #End
I'm also treating the start and end times as a semi-open interval with an inclusive start and exclusive end. This is normally the more sensible way to work with continuous data such as datetime.
Hopefully, you can see how I'm building up each CTE to get from the individual timestamps and piece counts to, for each minute, having a concept of how many pieces were produced in that minute. Once we've got to that point, we can just SUM over the minutes we want to include.

Group DateTime into 5,15,30 and 60 minute intervals

I am trying to group some records into 5-, 15-, 30- and 60-minute intervals:
SELECT AVG(value) as "AvgValue",
sample_date/(5*60) as "TimeFive"
FROM DATA
WHERE id = 123 AND sample_date >= 3/21/2012
i want to run several queries, each would group my average values into the desired time increments. So the 5-min query would return results like this:
AvgValue TimeFive
6.90 1995-01-01 00:05:00
7.15 1995-01-01 00:10:00
8.25 1995-01-01 00:15:00
The 30-min query would result in this:
AvgValue TimeThirty
6.95 1995-01-01 00:30:00
7.40 1995-01-01 01:00:00
The datetime column is in yyyy-mm-dd hh:mm:ss format
I am getting implicit conversion errors of my datetime column. Any help is much appreciated!
Using
datediff(minute, '1990-01-01T00:00:00', yourDatetime)
will give you the number of minutes since 1990-1-1 (you can use the desired base date).
Then you can divide by 5, 15, 30 or 60, and group by the result of this division.
I've cheked it will be evaluated as an integer division, so you'll get an integer number you can use to group by.
i.e.
group by datediff(minute, '1990-01-01T00:00:00', yourDatetime) /5
UPDATE As the original question was edited to require the data to be shown in date-time format after the grouping, I've added this simple query that will do what the OP wants:
-- This convert the period to date-time format
SELECT
-- note the 5, the "minute", and the starting point to convert the
-- period back to original time
DATEADD(minute, AP.FiveMinutesPeriod * 5, '2010-01-01T00:00:00') AS Period,
AP.AvgValue
FROM
-- this groups by the period and gets the average
(SELECT
P.FiveMinutesPeriod,
AVG(P.Value) AS AvgValue
FROM
-- This calculates the period (five minutes in this instance)
(SELECT
-- note the division by 5 and the "minute" to build the 5 minute periods
-- the '2010-01-01T00:00:00' is the starting point for the periods
datediff(minute, '2010-01-01T00:00:00', T.Time)/5 AS FiveMinutesPeriod,
T.Value
FROM Test T) AS P
GROUP BY P.FiveMinutesPeriod) AP
NOTE: I've divided this in 3 subqueries for clarity. You should read it from inside out. It could, of course, be written as a single, compact query
NOTE: if you change the period and the starting date-time you can get any interval you need, like weeks starting from a given day, or whatever you can need
If you want to generate test data for this query use this:
CREATE TABLE Test
( Id INT IDENTITY PRIMARY KEY,
Time DATETIME,
Value FLOAT)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:00:22', 10)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:03:22', 10)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:04:45', 10)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:07:21', 20)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:10:25', 30)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:11:22', 30)
INSERT INTO Test(Time, Value) VALUES('2012-03-22T00:14:47', 30)
The result of executing the query is this:
Period AvgValue
2012-03-22 00:00:00.000 10
2012-03-22 00:05:00.000 20
2012-03-22 00:10:00.000 30
Building on #JotaBe's answer (to which I cannot comment on--otherwise I would), you could also try something like this which would not require a subquery.
SELECT
AVG(value) AS 'AvgValue',
-- Add the rounded seconds back onto epoch to get rounded time
DATEADD(
MINUTE,
(DATEDIFF(MINUTE, '1990-01-01T00:00:00', your_date) / 30) * 30,
'1990-01-01T00:00:00'
) AS 'TimeThirty'
FROM YourTable
-- WHERE your_date > some max lookback period
GROUP BY
(DATEDIFF(MINUTE, '1990-01-01T00:00:00', your_date) / 30)
This change removes temp tables and subqueries. It uses the same core logic for grouping by 30 minute intervals but, when presenting the data back as part of the result I'm just reversing the interval calculation to get the rounded date & time.
So, in case you googled this, but you need to do it in mysql, which was my case:
In MySQL you can do
GROUP BY
CONCAT(
DATE_FORMAT(`timestamp`,'%m-%d-%Y %H:'),
FLOOR(DATE_FORMAT(`timestamp`,'%i')/5)*5
)
In the new SQL Server 2022, you can use DATE_BUCKET, this rounds it down to the nearest interval specified.
SELECT
DATE_BUCKET(minute, 5, d.sample_date) AS TimeFive,
AVG(d.value) AS AvgValue
FROM DATA d
WHERE d.id = 123
AND d.sample_date >= '20121203'
GROUP BY
DATE_BUCKET(minute, 5, d.sample_date);
You can use the following statement, this removed the second component and calculates the number of minutes away from the five minute mark and uses this to round down to the time block. This is ideal if you want to change your window, you can simply change the mod value.
select dateadd(minute, - datepart(minute, [YOURDATE]) % 5, dateadd(minute, datediff(minute, 0, [YOURDATE]), 0)) as [TimeBlock]
This will help exactly what you want
replace dt - your datetime c - call field astro_transit1 - your table 300 refer 5 min so add 300 each time for time gap increase
SELECT FROM_UNIXTIME( 300 * ROUND( UNIX_TIMESTAMP( r.dt ) /300 ) ) AS 5datetime, ( SELECT r.c FROM astro_transit1 ra WHERE ra.dt = r.dt ORDER BY ra.dt DESC LIMIT 1 ) AS first_val FROM astro_transit1 r GROUP BY UNIX_TIMESTAMP( r.dt ) DIV 300 LIMIT 0 , 30

How to design database table for working hours?

Hei guys, I'm trying to help my friend to design database tables. It is for a system tracking workers' working hours in a factory by reading card info from certain card readers. Each time a worker log his in/out information, there would be an record saved.
My problem is, how can I calculate each worker's working time (in minutes), each workday? A worker may work from 8:00AM~20:00PM, or 20:00PM~8:00AM.
Anyone can help me?
Thanks!
You guys did give me a lot of help.
The previous design is a table with in-record or out-record. It was hard for me to locate which ones belong to the same work-time-span. I now use another table with records both have the in-time and out-time in the same record. Insert to save in-time, update to save out-time, which makes it easy to calculate the total minutes between in-time and out-time.
SELECT datediff(hh,'2011-08-30 04:47','2011-08-30 05:48') as [Hour(s) Worked]
Hour(s) Worked
--------------
1
a simple example with 2 tables
[TblUsers]
User_id PK
FirstName
LastName
[TblSchedule]
Schedule_id PK
User_id FK
Date_From
Date_To
to get a daily work grid with times, you can write something like:
SELECT
u.FirstName + ' ' + u.LastName as [username],
CAST(FLOOR(CAST(#datetime as float)) as datetime) as [date],
DATEDIFF(minute, s.Date_To, s.Date_From) as [workMinutes]
FROM
[TblSchedule] s, [TblUsers] u
WHERE
s.user_id = u.user_id
GROUP BY
u.FirstName + ' ' + u.LastName,
CAST(FLOOR(CAST(#datetime as float)) as datetime)
ORDER BY
s.Date_From;
Just calculate the Minutes between each IN-Record and the following OUT-Record from this worker. If you want it for a whole day then fetch the relevant records and sum up the relevant differences.
The more complex thing here is when some worker forget about stamping. Your program have to be prepared for such cases.
Also be aware of things like daylight saving time. Time-Calcs can be very complicated.
I think I would do calculation on application level and not in SQL in this case.
DATEDIFF can give you some strange results.
For example take this two DATETIME2 (I presume you have SQL Server 2008) values that have a difference of 5 minutes:
SELECT DATEDIFF(hh,'2011-01-01 04:59:00','2011-01-01 05:04:00')
Results
-----------
1
The result is somehow strange: 1 hour. Strange, because the difference in minutes is 5 minutes but the difference in hours is 1 hour and we know that 1 hour = 60 minutse. Please read this article to see the explanations.
Solutions:
1) Instead of DATEDIFF(hh,...) use DATEDIFF(mi,...)
Ex:
SELECT DATEDIFF(mi,'2011-01-01 07:55:00','2011-01-01 16:02:00') [Minutes]
,DATEDIFF(mi,'2011-01-01 07:55:00','2011-01-01 16:02:00')/60 [Hours]
--8 hours
,DATEDIFF(mi,'2011-01-01 07:55:00','2011-01-01 16:02:00')%60 [Additional minute]
--7 minute
But:
SELECT DATEDIFF(mi,'2011-01-01 08:00:59','2011-01-01 16:00:05') [Minutes]
--480
,DATEDIFF(ss,'2011-01-01 08:00:59','2011-01-01 16:00:05')/60 [Seconds/60]
--479
2) Instead of using DATEDIFF function (with DATETIME[2][OFFSET] data types) use DATETIME values with the - operator:
DECLARE #Test TABLE
(
TestId INT IDENTITY(1,1) PRIMARY KEY
,[Enter] DATETIME NOT NULL
,[Exit] DATETIME NOT NULL
);
INSERT #Test
VALUES ('2011-01-01 07:55:00','2011-01-01 16:02:02')
,('2011-01-01 08:00:59','2011-01-01 16:00:05');
SELECT *
,t.[Exit] - t.[Enter] AS MyDateDiff
,DATEPART(hh,t.[Exit] - t.[Enter]) [Hours]
,DATEPART(mi,t.[Exit] - t.[Enter]) [Additional minutes]
,DATEPART(ss,t.[Exit] - t.[Enter]) [Additional seconds]
FROM #Test t
Results:
TestId Enter Exit MyDateDiff Hours Additional minute Additional seconds
----------- ----------------------- ----------------------- ----------------------- ----------- ----------------- ------------------
1 2011-01-01 07:55:00.000 2011-01-01 16:02:02.000 1900-01-01 08:07:02.000 8 7 2
2 2011-01-01 08:00:59.000 2011-01-01 16:00:05.000 1900-01-01 07:59:06.000 7 59 6