I have the following issue:
I have a datetime field, which contains entries like this: "1970-01-01 22:09:26.000"
I would like to extract only the 22:09:26 (hh:mm:ss) part, but I am unable to convert it into 24h format, I used FORMAT and CONVERT, but received the the am/pm culture (for the CONVERT I tried to use 13 culture value).
What is the simplest way to construct the formula to give back the above mentioned format?
Thank you!
1st way
You can select the format you wish from https://www.mssqltips.com/sqlservertip/1145/date-and-time-conversions-using-sql-server/
select replace(convert(nvarchar(20), CAST('1970-01-01 22:09:26.000' AS datetime), 114),'-',':')
2nd way
It is not a conversion,but if your entries are all the same format then you can use the below:
select right('1970-01-01 22:09:26.000',12)
Updated if you have null dates as well:
1.
select case when value is not null
then replace(convert(nvarchar(20), CAST(value AS datetime), 114),'-',':')
else null
end
select case when value is not null then right(value,12)
else null end
To get just the time portion of a datetime you just need to cast or convert it into the appropriate data type. If you really want to be formatting your data right in the query, this is very possible with format and I am not sure what issues you were facing there:
declare #t table(d datetime);
insert into #t values(dateadd(minute,-90,getdate())),(dateadd(minute,-60,getdate())),(dateadd(minute,-30,getdate())),(dateadd(minute,90,getdate()));
select d
,cast(d as time) as TimeValue
,format(d,'HH:mm:ss') as FormattedTimeValue
from #t;
Output
+-------------------------+------------------+--------------------+
| d | TimeValue | FormattedTimeValue |
+-------------------------+------------------+--------------------+
| 2020-08-10 11:51:15.560 | 11:51:15.5600000 | 11:51:15 |
| 2020-08-10 12:21:15.560 | 12:21:15.5600000 | 12:21:15 |
| 2020-08-10 12:51:15.560 | 12:51:15.5600000 | 12:51:15 |
| 2020-08-10 14:51:15.560 | 14:51:15.5600000 | 14:51:15 |
+-------------------------+------------------+--------------------+
By using format code as 108 we can get datetime in 'HH:mm:ss' format.
DECLARE #now DATETIME = GETDATE()
SELECT CONVERT(NVARCHAR(20), #now, 108)
Related
I have an ID column, and a time column. I want to group the IDs by average time.
IDs: 1234, 1234, 5678, 5678
Times: 13:21, 19:55, 14:25, 15:04
select ID,
avg(cast(CONCAT(left(cast(Time as varchar),2),substring(cast(Time as varchar),4,2)) as int)*1.0)
It does return a result, but I don't believe the average to be correct as the average time can be outside of normal time constraints (aka the minutes can be > 59).
time stores a point in time, not a duration. What would you do for a duration longer than a day? You should instead store either the duration in seconds, minutes, what have you, and format it as hh:mm etc. when you want to display it. Or better yet, store a start date and end date, which is more complete information, and you can always derive the duration (in whatever format you like) from that.
Anyway, dealing with what you have, and assuming this table and sample data:
CREATE TABLE dbo.BadChoices
(
ID int,
DurationWithWrongType time(0)
);
INSERT dbo.BadChoices(ID, DurationWithWrongType) VALUES
(1234, '13:21'),
(1234, '19:55'),
(5678, '14:25'),
(5678, '15:04');
You could I suppose do:
SELECT ID, AvgDuration = CONVERT(DECIMAL(10,2),
AVG(DATEDIFF(MINUTE, '00:00', DurationWithWrongType)*1.0))
FROM dbo.BadChoices
GROUP BY ID;
Output:
ID
AvgDuration
1234
998.00
5678
884.50
Example db<>fiddle
If you want the display to be HH:MM, and you know for sure your durations will always be < 24 hours, you could do:
;WITH src AS
(
SELECT ID, AvgDuration = CONVERT(DECIMAL(10,2),
AVG(DATEDIFF(MINUTE, '00:00', DurationWithWrongType)*1.0))
FROM dbo.BadChoices
GROUP BY ID
)
SELECT ID, AvgDuration,
AvgDurHHMMSS = CONVERT(time(0), DATEADD(SECOND, AvgDuration*60, '00:00'))
FROM src;
Output:
ID
AvgDuration
AvgDurHHMMSS
1234
998.00
16:38:00
5678
884.50
14:44:30
Example db<>fiddle
We have to cast to datetime to be able to cast to float. We can then find the average and cast back to datetime and then back to time.
A second alternative is to convert the time into minutes, get the average and then use dateadd() and cast back to time
create table times(
t time);
insert into times values
('13:21'),
('19:55'),
('14:25'),
('15:04');
GO
4 rows affected
select
cast(
cast(
avg(
cast(
cast(t
as datetime)
as float)
)
as datetime)
as time)
from times
GO
| (No column name) |
| :--------------- |
| 15:41:15 |
select
cast(
dateadd(second,
avg(
DateDiff(second,0,t)
),
2000-01-01)
as time)
from times
GO
| (No column name) |
| :--------------- |
| 15:41:15 |
db<>fiddle here
I want the count of number of days from 25/02/2019 in month of February and expected result is 4
I tried using master..spt_values in sql server but did not get expected result
declare #fdays int ,#d date=cast('20190201' as date),#JoinDate date=cast('20190225' as date)
select count(dateadd(dd,number,#d)) from master..spt_values
where type = 'p'
and month(dateadd(dd,number,#d))=month(#d)
and year(dateadd(dd,number,#d))=year(#d)
and cast(GETDate() as date)>= Cast(dateadd(dd,number,#JoinDate) as date )
The result of above code is 28 but I want 4
Please help me to find the expected result
This is simple date arithmetic, you do not need to use spt_values:
declare #d date = '20190225';
select datediff(month,0,#d) as MonthsDiff -- Months since an arbitrary date
,dateadd(month,datediff(month,0,#d)+1,0) as StartOfFollowingMonth -- Add months above +1 to same arbitrary date
,datediff(day,#d,dateadd(month,datediff(month,0,#d)+1,0)) as DaysBetweenGivenDate -- DATEDIFF between given date and start of month from above;
Output:
+------------+-------------------------+----------------------+
| MonthsDiff | StartOfFollowingMonth | DaysBetweenGivenDate |
+------------+-------------------------+----------------------+
| 1429 | 2019-03-01 00:00:00.000 | 4 |
+------------+-------------------------+----------------------+
Try this:
declare #date date='20140603'
select datediff(day, #date, dateadd(month, 1, #date))-day(#date)
Starting with SQL Server 2012, you could just use the EOMONTH function:
SELECT DATEDIFF(DAY, '20190225', EOMONTH ('20190225')) + 1 [thedays]
= 4.
I need date format in American Format i.e. 9/30/2018; 8/31/2018; 7/31/2018.. so on and so forth in SSIS. I have written the code in the format as
LEFT((DT_STR,50,1252)DATEADD("d",-DAY(GETDATE()),GETDATE()),10)
This is bringing date in 2018-09-30 which is not the proper format. I do have given the data type as "STRING" as the above code doesn't take "DATE/DATE-TIME" as data type.
I am trying to bring the previous month last date and hence the format currently being fetched is not right.
Any guesses?
Thanks!
For a format like this, the date parts will need to be extracted from the date and concatenated accordingly. The expression below will convert the date to the DD/MM/YYYY format. Since you only listed single digits for the month in your question, this example does not account for zeros and the length will vary. If you want zeros added to single digit days and months, a "0" (with quotes) will need to be appended before the day and month.
RIGHT((DT_STR, 2, 1252) DATEPART("MM", DATEADD("D",-DAY(GETDATE()),GETDATE())), 2)
+ "/" + RIGHT((DT_STR, 2, 1252) DATEPART("DD", DATEADD("D",-DAY(GETDATE()),GETDATE())), 2)
+ "/" + (DT_STR, 4, 1252) DATEPART("YYYY", DATEADD("D",-DAY(GETDATE()),GETDATE()))
How about that
DECLARE #AsStr VARCHAR(10) = '2018-09-30', --If you store it as string
#AsDate DATE = '2018-09-30'; --If you store it as date
SELECT CONVERT(VARCHAR(10), #AsDate, 101) AsStr,
CONVERT(VARCHAR(10), CAST(#AsStr AS DATE), 101) AsDate;
Returns
+------------+------------+
| AsStr | AsDate |
+------------+------------+
| 09/30/2018 | 09/30/2018 |
+------------+------------+
Or you can use FORMAT() function as
SELECT
FORMAT(CAST(#AsStr AS DATE), 'MM/dd/yyyy') FormatStr,
FORMAT(#AsDate, 'MM/dd/yyyy') FormatDate;
Returns
+------------+------------+
| FormatStr | FormatDate |
+------------+------------+
| 09/30/2018 | 09/30/2018 |
+------------+------------+
You can use DATEFROMPARTS to get the first day of the month fairly easily. Then, you can use DATEADD to subtract a day, then CONVERT to output the 101 format which is in the form MM/DD/YYYY.
For example:
DECLARE #DT_STR Date = '2018-10-23'
SELECT CONVERT(varchar, DATEADD(DAY, -1, DATEFROMPARTS(YEAR(#DT_STR), MONTH(#DT_STR), 1)), 101) AS [answer]
Produces output:
answer
09/30/2018
I have a table named TimeList:
| Slot |
==============
| 10:00 |
| 11:00 |
| 12:00 |
| 13:00 | and so on
That saves the Times in Varchar(5)
The desired result should be showing the rows with time that is more than the current time, for example if the current time is 11:12 A.M. the result should return:
| Slot |
==============
| 12:00 |
| 13:00 |
I tried to Convert the two values into time and comparing them with:
SELECT *
FROM TimeList
WHERE Convert(Time, Slot) > Convert(Time, GETDATE())
But it didn't work saying that Time is not a recognizable format in SQL
Is there anyway I could compare the two time slots?
Depends on the version of SQL Server you're running, I think. There is a CAST(.. as time) in 2012 or later, but I think that's a fairly new development. So... to compare the current date/time with the Timelist where the times are converted to "time, if it were today," something like this should work :
SELECT *
FROM TimeList
WHERE Convert(Datetime, FORMAT (GETDATE(), 'd') + ' ' + Slot) > GETDATE()
Conversely, if you want to compare the times to the current time, as text:
SELECT *
FROM TimeList
WHERE Slot > FORMAT(GETDATE(), N'hh\:mm')
Try This.....
SELECT *
FROM TimeList
WHERE Slot > CONVERT(time,GETDATE())
Thank you very much for all the answers, fortunately I found the answer to my question inspired by your answers.
The solution is:
SELECT *
FROM TimeList
WHERE Slot > CONVERT(varchar(5),GETDATE(), 108)
Where it seems that 108 is the format for time saved as char/varchar in which Slot was categorized as too
I am trying to determine on a per row basis, how many of these requests exist at a specific moment in time.
The date and time are formatted specifically to appear this way in the results, however they are stored in default yyyy-mm-dd and 00:00:00.000 formats respectively in the database
Request Data:
ID | CDate | CTime | LDate | LTime
---------------------------------------------------------------
230700 | 13/07/2016 | 6:52am | 13/07/2016 | 7:21am
746970 | 13/07/2016 | 7:05am | 13/07/2016 | 7:10am
746971 | 13/07/2016 | 7:09am | 13/07/2016 | 7:09am
746972 | 13/07/2016 | 7:16am | 13/07/2016 | 7:27am
746973 | 13/07/2016 | 7:20am | 13/07/2016 | 7:29am
CTime refers to Issue Creation time, with LTime referring to the time the issue has been logged into by a user.
I wish to add a new column at the end of these results, based on the results of the entire query. The new column would count how many issues are visible at any given time. Issues are visible as soon as they are created, and disappear when a user logs into the request, creating an LTime entry.
In this example, we will use the 2nd row of data for ID: 746970. We can see that the creation time was 7:05am, however the issue wasn't logged into until 7:10am. At that login time, 2 other issues had already been created, however hadn't yet been logged into (230700 and 746971), with a creation time of 6:52am/7:21am and 7:09am/7:09am respectively. As such, the new column would report a value of 3 for number of issues visible at the time of logging in.
My thought process so far leads me to believe this would need a 2-3 part query, potentially storing results in a Temp Table. The first part of the query would obtain the results as they are shown above (already created). The second part of the query would determine on a 'per row' basis how many rows have a CTime less than the each row's LTime. The 3rd query would then run another check on the results of the 2nd query to count the number of rows where the LTime of the current row is equal to or less than the LTime of other rows.
The results upon running this would appear as below. The bracketed text would not show in the results, merely included to show working.
New data:
ID | CDate | CTime | LDate | LTime | #Active
----------------------------------------------------------------------------
230700 | 13/07/2016 | 6:52am | 13/07/2016 | 7:21am | 3 (230700, 746972, 746973)
746970 | 13/07/2016 | 7:05am | 13/07/2016 | 7:10am | 2 (230700, 746970)
746971 | 13/07/2016 | 7:09am | 13/07/2016 | 7:09am | 3 (230700, 746970, 746971)
746972 | 13/07/2016 | 7:16am | 13/07/2016 | 7:27am | 2 (746972, 746973)
746973 | 13/07/2016 | 7:20am | 13/07/2016 | 7:29am | 1 (746973)
I'm at a loss on this one, I know the logic for it, but can't for the life of me put it into MS SQL code. Any assistance would be greatly appreciated.
The first thing I'm going to say is that you should consider altering your table such that the DATE and TIME fields are not separate. Unless you're running a whole bunch of queries that only care about time or only care about date and you have indexes built around this, you're better off using a single DATETIME column - it makes running queries that require both date and time so much easier.
Moving on to what I believe is the solution to your problem (assuming the criteria for "active" is correct)... The short answer is that you don't need any temporary tables or anything (my solution uses a CTE but it doesn't even need that):
DECLARE # TABLE (ID INT PRIMARY KEY, CDate DATE NOT NULL, CTime TIME NOT NULL, LDate DATE NOT NULL, LTime TIME NOT NULL)
INSERT # VALUES (230700, '2016-07-13', '06:52:00.000', '2016-07-13', '07:21:00.000')
, (746970, '2016-07-13', '07:05:00.000', '2016-07-13', '07:10:00.000')
, (746971, '2016-07-13', '07:09:00.000', '2016-07-13', '07:09:00.000')
, (746972, '2016-07-13', '07:16:00.000', '2016-07-13', '07:27:00.000')
, (746973, '2016-07-13', '07:20:00.000', '2016-07-13', '07:29:00.000');
WITH CTE AS (
SELECT ID
, CDate + CAST(CTime AS DATETIME) CDateTime
, LDate + CAST(LTime AS DATETIME) LDateTime
FROM #)
SELECT T.ID, T.CDateTime, T.LDateTime, Z.ActiveCount
FROM CTE T
OUTER APPLY (
SELECT CAST(COUNT(*) AS VARCHAR(255))
+ ' (' +
STUFF((SELECT ', ' + CAST(ID AS VARCHAR(255))
FROM CTE
WHERE CDateTime <= T.LDateTime
AND LDateTime >= T.LDateTime
ORDER BY ID
FOR XML PATH ('')), 1, 2, '') + ')'
-- COUNT(*) -- this just produces the number
FROM CTE
WHERE CDateTime <= T.LDateTime
AND LDateTime >= T.LDateTime) Z(ActiveCount)
If you need to keep the date and time fields separate, the statement can be rewritten as:
DECLARE # TABLE (ID INT PRIMARY KEY, CDate DATE NOT NULL, CTime TIME NOT NULL, LDate DATE NOT NULL, LTime TIME NOT NULL)
INSERT # VALUES (230700, '2016-07-13', '06:52:00.000', '2016-07-13', '07:21:00.000')
, (746970, '2016-07-13', '07:05:00.000', '2016-07-13', '07:10:00.000')
, (746971, '2016-07-13', '07:09:00.000', '2016-07-13', '07:09:00.000')
, (746972, '2016-07-13', '07:16:00.000', '2016-07-13', '07:27:00.000')
, (746973, '2016-07-13', '07:20:00.000', '2016-07-13', '07:29:00.000');
SELECT T.ID
, CONVERT(VARCHAR(255), T.CDate, 103) CDate
, CONVERT(VARCHAR(255), T.CTime, 100) CTime
, CONVERT(VARCHAR(255), T.LDate, 103) LDate
, CONVERT(VARCHAR(255), T.LTime, 100) LTime
, Z.ActiveCount
FROM # T
OUTER APPLY (
SELECT COUNT(*) -- this just produces the number
FROM #
WHERE CDate + CAST(CTime AS DATETIME) <= T.LDate + CAST(T.LTime AS DATETIME)
AND LDate + CAST(LTime AS DATETIME) >= T.LDate + CAST(T.LTime AS DATETIME)) Z(ActiveCount)