Get hour from datetime and put "Days" in field - sql-server

Using SQL Server, I have the following:
SELECT DATENAME(DW, DATEADD(DAY, 0, #mindate)) AS Day_of_Week ,
#crewon AS Crew_On ,
ISNULL(SUM(( CONVERT(NUMERIC(8, 2), C.FinishLength) / 2000 )), 0) AS Finished_Tons ,
ISNULL(SUM(CASE WHEN C.RepairCode LIKE '%A%'
THEN ( CONVERT(NUMERIC(8, 2), C.FinishLength)
/ 2000 )
END), 0) AS A_Tons
FROM NYS2SawPieces C
WHERE RIGHT(C.ShiftIdent, 1) = #crewon
AND C.ProdTime >= #mindate
AND C.ProdTime <= DATEADD(DAY, 1, #mindate)
I need to get just the "Hours" from ProdTime that are from 06 to 18 and put "Days" in the column. Hours outside that range would be "Nights".
Output would like this but add the column for shift.
Day_of_Week Crew_On Finished_Tons A_Tons Shift
Sunday A 0.000000 0.0000000 Days

Use a CASE statement to apply conditional logic to determine the output:
SELECT Col1, Col2,
CASE WHEN DATEPART(HOUR,C.ProdTime) BETWEEN 6 AND 18 THEN 'DAYS'
ELSE 'NIGHTS'
END AS Shift
FROM....
This will output a new column called Shift that will output DAYS if the value is between 6 and 8, otherwise it will output NIGHTS.

You can use the WHERE and between sql commands to help you filter your query by the hour, something along the lines of:
WHERE hour(your_datetime_field) between 6 and 18
The WHERE acts as a filter in selecting data that is returned in your query. The rest of the statement is giving the bounds for the hours you want your_datetime_field to be between.

Related

Convert number of hours to days and hours in SQL Server (NOT T-SQL)

I have a number of hours which I need to display in the format of days and hours.
This number is derived from a DATEDIFF instruction.
For numbers less than 24, I wish to display only hours - ie, 21 hours.
For larger numbers, I wish to display days and hours - ie, 3 days, 14 hours
I do not need to display any smaller unit than hours, and values should be rounded down to the preceding hour, so 1 hour and 59 minutes will be 1 hour.
I cannot use a stored procedure - this must run as a single select statement.
I am aware that I can calculate the value by using modulo, so assuming 71 hours:
select concat((71 - (71 % 24)) / 24, ' days, ', 71 % 24, ' hours')
This however is somewhat messy, and as the statement must be a single select, I will have to calculate the DATEDIFF 3 times as below.
SELECT CONCAT (
(DATEDIFF(HOUR, StartDate, EndDate) -
(DATEDIFF(HOUR, StartDate, EndDate) % 24)) / 24,
' days, ',
DATEDIFF(HOUR, StartDate, EndDate) % 24,
' hours')
FROM RecordsTable
Is it possible to either format a number of hours as days and hours directly using an inbuilt SQL command, or failing that, select (datediff(hour, StartDate, EndDate) into a variable which I can reuse in the single select?
EDIT - As suggested, the solution was to use a CTE as follows:
WITH totalhours (htotal) AS
(
SELECT
DATEDIFF(HOUR, StartDate, EndDate) AS htotal
FROM
RecordsTable
)
SELECT
CONCAT ((htotal - (htotal % 24)) / 24,
' days, ',
htotal % 24,
' hours')
FROM
RecordsTable;
Use a CTE to generate your total once, and reference that total in your select against the CTE. Or use a subquery to generate the total once and then select from the subquery to get the desired results.
The fundamental issue is you need to materialize the total once to be able to reference it; forcing the engine to materialize a value is generally done via a CTE or subquery.
You can do a lot with datetime objects and format strings or datepart. For example,
declare #n int = 105;
select format(dateadd(day, -1, dateadd(hour, #n, '1753-1-1')), 'd h');
-- 4 9
Taking the minimum datetime value (1753-01-01), adding the requisite number of hours, subtracting one day (because on the first day you want days = 0), and then formatting.
You could improve the formatting like this:
select format(dateadd(day, -1, dateadd(hour, #n, '1753-1-1')), 'd \da\y(\s), h \hour(\s)');
-- 4 day(s), 9 hour(s)
Of course this will only work up to 31 days, because then you'll be out of the month of January in 1753 and into February. If that's the case, revert to datepart. This is uglier, but will work for larger values
select
datepart(day, (dateadd(day, -1, dateadd(hour, #n, '1753-1-1')))),
datepart(hour, (dateadd(day, -1, dateadd(hour, #n, '1753-1-1'))));

How to find out Date/Month interval between #StartDate and #EndDate passed in stored procedure parameters

I am working on a stored procedure where I am dividing the number of rows by interval of month and day repeated in the specified date range.
Interval Month and Day = 7th April and 8th October
Example
For Date range 2014/01/01 and 2014/12/31, 7th April and 8th October are repeated 2 times so I will divide my statement by 2.
For Date range 2014/01/01 and 2015/09/01, 7th April came 2 and 8th October 1 so I will divide my statement by 3.
As others have stated, the question is a bit unclear, but I believe I know what you're trying to do. You are trying to find the number of times that a set of dates (only taking Month/Day into account) happen over a range of dates (set by #StartDate and #EndDate). I think the select count(*) from TableName part of the question is a distraction, as you already know how to do that. Below is an answer on how to get the denominator, which is what you are trying to figure out how to do.
declare #StartDate date = '2014-01-01'
, #EndDate date = '2014-12-31'
, #DenVal int --Denominator Value
create table #dates_of_interest
(
month_nbr tinyint not null
, day_nbr tinyint not null
)
insert into #dates_of_interest
values (4, 7) --7th of April
, (10, 8) --8th of October
; with date_list as
(
--use a Recursive CTE to generate a list of all the dates in the given range.
select #StartDate as dt
union all
select dateadd(d,1,dt) as dt
from date_list
where 1=1
and dt < #EndDate
)
--Get the output of the Recursive CTE along with Month/Day numbes
select dt
, datepart(m,dt) as month_nbr
, datepart(d,dt) as day_nbr
into #list_of_dates
from date_list as dl
option (maxrecursion 32767) --set to max possible levels of recursion (might want to lower this number)
--Set the Denominator to the results of the sum(case/when) AKA countif
set #DenVal =
(
select sum(case when di.month_nbr is null and di.day_nbr is null then 0 else 1 end)
from #list_of_dates as ld
left join #dates_of_interest as di on ld.month_nbr = di.month_nbr
and ld.day_nbr = di.day_nbr
)
Print #DenVal
Both examples of 1/1/2014 - 12/31/2014 and 1/1/2014 - 9/1/2015 come up with the desired results of 2 and 3 respectively. There may be other ways of accomplishing this, but I thought that a Recursive CTE was the best option.

Difference of dates in Where Clause

I am using SQL server. I'm trying to ad a condition that gives me the rows where the difference is date is between 0-3 or if something was opened 0-3 days after the migration date.
When I added the condition to the WHERE clause it acting funcky. I need help figuring out the best way to do this
This give me a result where the date diff is less than 0 even though I say >= 0
select * from table_1
where datediff(day, a.[opened date], d.[UserMigratedDate]) >= 0
This give me a result where the date diff is greater than 4 even though I say < 4
select * from table_1
where datediff(day, a.[opened date], d.[UserMigratedDate]) < 4
When I use a between it does noting. Am I doing this wrong?
select * from table_1
where (datediff(day, a.[opened date], d.[UserMigratedDate]) >= 0 and datediff(day, a.[opened date], d.[UserMigratedDate]) < 4)
I would like to see your test data that's causing this... because the where clause is constructed correctly. Take the below case for example. ID 2-5 will be returned. Also, your first datediff() where you are seeing if the days are >=0 doesn't make sense to be unless someone could migrate something before it was opened...
http://rextester.com/UTLX59878
declare #table table (id int, openedDate datetime, UserMigratedDate datetime)
insert into #table
values
(1,'2017-01-01','2016-12-31'), --this technically shouldn't happen
(2,'2017-01-01','2017-01-01'),
(3,'2017-01-01','2017-01-02'),
(4,'2017-01-01','2017-01-03'),
(5,'2017-01-01','2017-01-04'),
(6,'2017-01-01','2017-01-05')
select
*,
datediff(day, openedDate, UserMigratedDate) as theDateDiff
from #table
where
datediff(day, openedDate, UserMigratedDate) >= 0
and
datediff(day, openedDate, UserMigratedDate) < 4

Group data rows by near time

Here is the problem I am facing:
I got a large table containing rows, I want to group them by near time, more specifically the time difference less than 2 minutes, example as following
With following input data:
A 16:01:01
B 16:01:20
C 16:14:02
D 16:15:01
E 16:20:02
the expected result is
16:01:01 2
16:14:02 2
16:20:02 1
If you're using SQL server 2012, you'r in luck and you can use lag function and rolling total sum:
with cte as (
select
case
when datediff(mi, lag(data) over (order by data), data) <= 1 then 0
else 1
end as ch,
data
from test
), cte2 as (
select
data, sum(ch) over (order by data) as grp
from cte
)
select
min(data) as data, count(*) as cn
from cte2
group by grp
sql fiddle demo
SELECT CONVERT(VARCHAR(8),
DATEADD(minute, (DATEDIFF(n, 0, time) / 2) * 2, 0),
108),
COUNT(*)
FROM times
GROUP BY DATEDIFF(n, 0, time) / 2
Explanation:
CONVERT displays a DateTime in hh:mm:ss format (= 108).
DATEDIFF converts to minutes and then divides by two, rounding to an integer so each GROUP of 2 minutes resolves to the same integer.
DATEADD is used to convert this number of minutes back to a DateTime, having multiplied by 2 to get back to the correct (rounded) time.
See SQL Fiddle Demo
Declare #m_TestTable table
(
DateRecorded datetime
)
Insert into #m_TestTable Values ('16:01:01' )
Insert into #m_TestTable Values ('16:01:20' )
Insert into #m_TestTable Values ('16:14:02' )
Insert into #m_TestTable Values ('16:15:01' )
Insert into #m_TestTable Values ('16:20:01' );
With tblDifference as
(
Select Row_Number() OVER (Order by DateRecorded) as RowNumber,DateRecorded from #m_TestTable
)
select cur.DateRecorded as prvD, prv.DateRecorded as prvC, dateDiff(n, cur.DateRecorded,prv.DateRecorded) from tblDifference cur LEFT OUTER JOIN tblDifference prv
ON cur.RowNumber = prv.RowNumber + 1
this will give you the time difference in minutes between 2 rows. You can select any row that has a time difference less then 2 mins. It will also give you the upper and lower value.
It should be usefull to find any values closer then 2 minutes apart.
prvD prvC Diff
1900-01-01 16:01:01.000 NULL NULL
1900-01-01 16:01:20.000 1900-01-01 16:01:01.000 0
1900-01-01 16:14:02.000 1900-01-01 16:01:20.000 -13
1900-01-01 16:15:01.000 1900-01-01 16:14:02.000 -1
1900-01-01 16:20:01.000 1900-01-01 16:15:01.000 -5

TS SQL - group by minute

I have a table with timestamps. What is the proper query to get the records counts for each minute for the last hour.
I.e. if now is 2:25, I want to know how many record were between 1:25 and 1:26, 1:26 and 1:27, and so on, so I have 60 results.
This will return a count of results for each minute (where you have records) in the last hour
SELECT DATEPART(n, time_stamp) AS minute, COUNT(*) as results
FROM table_name
WHERE time_stamp > DATEADD(hh, -1, GETDATE())
GROUP BY DATEPART(n, time_stamp)
This may return less than 60 results, depending on the data. If you have to have 60 results, the query is slightly different. This uses a Common Table Expression to generate a list of 60 numbers and a correlated sub-query to get the results for each minute:
WITH numbers ( num ) AS (
SELECT 1 UNION ALL
SELECT 1 + num FROM numbers WHERE num < 60 )
SELECT num AS minute,
(SELECT COUNT(*) AS results
FROM table_name
WHERE DATEPART(n, time_stamp) = num
AND time_stamp > DATEADD(hh, -1, GETDATE())
FROM numbers
To see the results, replace DATEADD(hh, -1, GETDATE()) with DATEADD(mi, -15, GETDATE()) and you'll get the results for the last 15 minutes and 0 for other minutes.
This is an alternative I have found useful for determining how many records are inserted or updated per minute. The nice thing about having your date format as a variable up front is that you can easily change it to analyze per hour instead. Hope this helps!
DECLARE #dateFormat as varchar(max) = 'yyyy-MM-dd HH:mm'
SELECT format(timeColumn, #dateFormat) AS minute, COUNT(*) as results
FROM yourTable
WHERE timeColumn > DATEADD(hh, -1, GETDATE())
GROUP BY format(timeColumn, #dateFormat)
ORDER BY 1
As you edited the question, I edit my answer. If I have understood you correctly, you want to look only at the past hour - that is, a timespan from one hour before the request is made to the current time. This is how I'd do it:
SELECT
COUNT(yourTimeStamp)
FROM yourTable
WHERE DATEADD('hh', -1, GetDate()) <= yourTimeStamp
AND yourTimeStamp < GetDate()
GROUP BY DATEPART('mm', yourTimeStamp)
I am not entirely sure that the syntax is exact. When coding in MSSQL, I would use the CURRENT_TIMESTAMP for the current time, MINUTE instead of DATEPART etc, but you get the idea for the solution.
DATEPART is what you're looking for:
declare #times table
(
someTime datetime
)
INSERT INTO #Times (sometime) values ('jan 12 2008 12:23')
INSERT INTO #Times (sometime) values ('jan 12 2008 12:34')
INSERT INTO #Times (sometime) values ('jan 12 2008 12:35')
INSERT INTO #Times (sometime) values ('jan 12 2008 12:25')
INSERT INTO #Times (sometime) values ('jan 12 2008 12:02')
INSERT INTO #Times (sometime) values ('jan 12 2008 12:09')
INSERT INTO #Times (sometime) values ('jan 12 2008 12:35')
select DATEPART(mi,sometime) AS Minute, count(*) AS NumOccurances
from #Times
WHERE SomeTime BETWEEN #Lower AND #Upper
GROUP BY DATEPART(mi, sometime)
order by NumOccurances DESC
Result:
Minute NumOccurances
35 2
2 1
9 1
23 1
25 1
34 1
If you want to group results by minute, then you can use a formatted string. This will group by number of minutes since 1/1/1900 not minute within day.
WITH formatted AS (
SELECT FORMAT(<your_datetime_column>, 'yyyy-MM-dd HH:mm') AS minute_text
FROM <your_table>
)
SELECT minute_text, COUNT(*) AS number_of_rows
FROM formatted
GROUP BY minute_text
ORDER BY 1 DESC
Here's my fixed up version of Robin's answer. I made it output the errors in the correct order and output the time as well instead of just the number which isn't super useful if you're charting this out.
WITH numbers ( num ) AS (
SELECT 1 UNION ALL
SELECT 1 + num FROM numbers WHERE num < 60 )
SELECT (SELECT DATEADD(n, -num, GETDATE())) AS TimeStamp,
(SELECT COUNT(*) AS results
FROM ErrorLogs
WHERE DATEPART(n, TimeStamp) = DATEPART(n, DATEADD(n, -num, GETDATE()))
AND TimeStamp > DATEADD(hh, -1, GETDATE())) as Count
FROM numbers
SELECT COUNT (TS) from TABLE where TABLE.TS BETWEEN(starttime, endtime)

Resources