How to convert datetime string without delimiters in SQL Server as datetime? - sql-server

I am looking to convert a string like this: 20160520191959550 - that is actually date-time for 2016-05-20 19:19:59.
I tried using CAST as datetime in a SQL statement, but got this error:
Conversion failed when converting date and/or time from character string.
Here is the SQL:
Select Cast(vbon.DATE_INS As datetime) As DINS
From vbon
I routinely use CAST on date strings without delimiters and am looking for a similar solution. Is there a simple way to get the job done without reverting to LEFT, RIGHT and SUBSTRING?

Addtional variant to already posted:
SELECT CONVERT(DATETIME, FORMAT(CONVERT(BIGINT, '20160520191959550'),
'####-##-## ##:##:##"."###'))

In SQL Server 2012 and later, you can use DATETIMEFROMPARTS:
DECLARE #DateTimeString varchar(19) = '20160520191959550';
SELECT DATETIMEFROMPARTS(
SUBSTRING(#DateTimeString,1,4)
, SUBSTRING(#DateTimeString,5,2)
, SUBSTRING(#DateTimeString,7,2)
, SUBSTRING(#DateTimeString,9,2)
, SUBSTRING(#DateTimeString,11,2)
, SUBSTRING(#DateTimeString,13,2)
, SUBSTRING(#DateTimeString,15,3)
);
In earlier versions, one method is to build a ISO 8601 formatted string and use CAST or CONVERT:
SELECT CAST(
SUBSTRING(#DateTimeString,1,4)
+ '-' + SUBSTRING(#DateTimeString,5,2)
+ '-' + SUBSTRING(#DateTimeString,7,2)
+ 'T' + SUBSTRING(#DateTimeString,9,2)
+ ':' + SUBSTRING(#DateTimeString,11,2)
+ ':' + SUBSTRING(#DateTimeString,13,2)
+ '.' + SUBSTRING(#DateTimeString,15,3)
AS datetime2(3));

SELECT CAST(
STUFF(
STUFF(
STUFF(
STUFF('20160520191959550', 9, 0, ' ')
, 12, 0, ':')
, 15, 0, ':')
, 18, 0, '.') AS DATETIME)

I suggest you to create function:
CREATE FUNCTION dbo.datestringtodatetime
(
-- Add the parameters for the function here
#d NVARCHAR(max)
)
RETURNS datetime
AS
BEGIN
-- Declare the return variable here
DECLARE #table TABLE (
id int,
[pos] int,
[sym] char(1)
)
DECLARE #output datetime
--In this table we determine were to put delimeters
INSERT INTO #table VALUES
(1, 5,'-'),
(2, 8,'-'),
(3, 11,'T'),
(4, 14,':'),
(5, 17,':'),
(6, 20,'.')
;WITH cte AS (
SELECT STUFF(#d,t.[pos],0,t.[sym]) as d_, 1 as l
FROM #table t
WHERE t.id = 1
UNION ALL
SELECT STUFF(c1.d_,t.[pos],0,t.[sym]), l+1
FROM cte c1
INNER JOIN #table t
ON t.id = l+1
)
SELECT #output = TRY_CAST(d_ as datetime)
FROM cte
WHERE l = 6
-- Return the result of the function
RETURN #output
END
GO
You can call it like:
SELECT [dbo].[datestringtodatetime] ('20160520191959550')
Output:
2016-05-20 19:19:59.550
If you pass it wrong value you will get NULL

I think convert would work for you. I can't test right now but I have done what you are looking for with dates. Here is a good article:
http://www.sqlusa.com/bestpractices/datetimeconversion/

You can try this:
SELECT CONVERT(VARCHAR(8), vbon.DATE_INS, 112) + REPLACE(CONVERT(varchar, vbon.DATE_INS, 108), ':','')
Ref: Convert DateTime to yyyyMMddHHmm in T-SQL

Related

Remove all Non-Numeric Chars from String

I would like to truncate all characters in a column, no matter where they are.
Example:
"+49123/4567890(testnumber)"
Should be changed to
"491234567890"
Is there a way without doing a replace for each char?
I have tried to replace it with several, but it is very time-consuming.
As you mentioned, if you are expecting only [a-zA-z()/+], you can use the translate function which is available from 2017+
declare #table TABLE (str varchar(max))
insert into #table
select '+49123/4567890(estnumber)'
select replace(translate(str, '/+()abcdefghijklmnopqrstuvwxyz', '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'), '~', '') digits
from #table
For more complex scenarios where the characters are not known, you can try using recursive CTE on a string column to extract only digits like following query.
;with cte
as (
select v.txt originalstring
,v.txt
,convert(varchar(max), '') as digits
,1 as lev
from (
values ('+49123/4567890(testnumber)')
,('+234&*#$%!##')
) v(txt)
union all
select originalstring
,stuff(txt, 1, 1, '')
,(
case
when left(txt, 1) LIKE '[0-9]'
then digits + left(txt, 1)
else digits
end
)
,lev + 1
from cte
where txt > ''
)
select originalstring
,digits
from (
select c.originalstring
,c.digits
,row_number() over (partition by c.originalstring order by lev desc
) rn
from cte c
) t
where rn = 1
Output
originalstring digits
--------------- --------
+234&*#$%!## 234
+49123/4567890(testnumber) 491234567890
A set-based option that exists in SQL Server 2017+ is to utilise translate.
You can hopefully adapt the following to your specific use-case:
select col, Replace(Translate(col, r, Replicate('*', Len(r))), '*', '') Newcol
from t
cross apply(values(' ABCDEFGHIJKLMNOPQRSTUVWXYZ/\+()'))r(r);
Example DB<>Fiddle
Instead of hardcoding the list of "bad" characters you can use a double TRANSLATE to first get the unwanted characters and then plug that back into TRANSLATE.
DECLARE #table TABLE
(
str VARCHAR(max)
)
INSERT INTO #table
SELECT '+49123/4567890(testnumber) '
DECLARE #CharactersToKeep VARCHAR(30) = '0123456789'
SELECT REPLACE(TRANSLATE(str, bad_chars, REPLICATE('X', LEN(bad_chars + 'X') - 1)), 'X', '')
FROM #table
CROSS APPLY (SELECT REPLACE(TRANSLATE(str, #CharactersToKeep, REPLICATE(LEFT(#CharactersToKeep, 1), LEN(#CharactersToKeep))), LEFT(#CharactersToKeep, 1), '')) ca(bad_chars)

Need to calculate the sum of second using a function from a string variable using SQL

There is a column named as duration in a table called Adventurous.The column has values as below.In the column Suffix of 'H' is hours,Suffix of 'M' is minutes and Suffix of 'S' is seconds.How can we select the hours, minutes and seconds and convert all into seconds i.e sum of all the hours minutes and seconds in the form of seconds.
Duration
--------
PT10M13S
PT13M22S
PT1H2M18S
PT11S
i tried using substring and charindex as below and tried to create a function but i am getting error:
Declare #Duration varchar(30) ='PT16H13M42S', #Dur varchar(10)
Declare #hours int
declare #mins int
declare #secs int
declare #len int
select #len = len(substring (#Duration, 3, len(#Duration))), #Dur=substring (#Duration, 3, len(#Duration))
select #hours = charindex('H', #Dur)
select substring(#Dur, 1, #hours-1)
select #Duration=substring (#Dur, #hours+1, len(#Dur))
select #mins = charindex('M', #Duration)
select substring(#Duration, 1, #mins-1)
select #Dur=substring (#Duration, #mins+1, len(#Duration))
select #secs= charindex('S', #Dur)
select substring(#Dur, 1, #Secs-1)
select #len, #Dur, #Duration
example PT1H2M18S= 1*3600+2*60+18=3738
Try this:
Declare #t table (duration varchar(50))
insert into #t values ('PT1H2M18S')
select
convert(int,substring(duration,CHARINDEX('PT',duration)+2,(CHARINDEX('H',duration)-CHARINDEX('PT',duration))-2))*3600 +
convert(int,substring(duration,CHARINDEX('H',duration)+1,(CHARINDEX('M',duration)-CHARINDEX('H',duration))-1))*60 +
convert(int,substring(duration,CHARINDEX('M',duration)+1,(CHARINDEX('S',duration)-CHARINDEX('M',duration))-1))
from #t
Another possible approach is to transform Duration text input into a valid T-SQL expression ('PT1H2M18S' will be transformed into '1*3600+2*60+18*1+0'). After that, consider next two options:
Generate and execute a dynamic statement, which will evaluate each expression or
Define a function to make the calculations
Input:
CREATE TABLE #Data (
Duration varchar(50)
)
INSERT INTO #Data
(Duration)
VALUES
('PT10M13S'),
('PT13M22S'),
('PT1H2M18S'),
('PT100H'),
('PT11S')
Dynamic statement:
DECLARE #stm nvarchar(max)
SET #stm = N''
SELECT #stm = #stm +
CONCAT(
'UNION ALL SELECT ''',
Duration,
''' AS [Duration], ',
REPLACE(REPLACE(REPLACE(REPLACE(Duration, 'H', '*3600+'), 'M', '*60+'), 'S', '*1+'), 'PT', ''),
'0 AS [Seconds] '
)
FROM #Data
SET #stm = STUFF(#stm, 1, 10, N'')
EXEC (#stm)
User-defined function:
CREATE FUNCTION [udfCalculateHMS] (#expression varchar(100))
RETURNS int
AS
BEGIN
DECLARE #result int
DECLARE #s varchar(100)
--
SET #result = 0
WHILE (CHARINDEX('+', #expression) > 0) BEGIN
SET #s = SUBSTRING(#expression, 1, CHARINDEX('+', #expression) - 1)
SET #expression = STUFF(#expression, 1, CHARINDEX('+', #expression), '')
SET #result = #result +
CONVERT(int, SUBSTRING(#s, 1, CHARINDEX('*', #s) - 1)) *
CONVERT(int, STUFF(#s, 1, CHARINDEX('*', #s), ''))
END
-- Return value
RETURN #result
END
SELECT
Duration,
dbo.udfCalculateHMS(CONCAT(REPLACE(REPLACE(REPLACE(REPLACE(Duration, 'H', '*3600+'), 'M', '*60+'), 'S', '*1+'), 'PT', ''), '0')) AS Seconds
FROM #Data
Output:
Duration Seconds
PT10M13S 613
PT13M22S 802
PT1H2M18S 3738
PT100H 360000
PT11S 11
This is how I would move across the string the pull out the correct integer values. The number of characters to offset may change depending on if you can have varying numbers of characters per hour, minute and second. But the principle should get you going.
Declare #Duration varchar(30) ='PT16H13M42S'
select * from
(values(substring(#Duration,CHARINDEX('PT',#duration)+2,(CHARINDEX('H',#Duration)-CHARINDEX('PT',#Duration))-2),
substring(#Duration,CHARINDEX('H',#duration)+1,(CHARINDEX('M',#Duration)-CHARINDEX('H',#Duration))-1),
substring(#Duration,CHARINDEX('M',#duration)+1,(CHARINDEX('S',#Duration)-CHARINDEX('M',#Duration))-1))) duration ([Hours], [Minutes], [Seconds]);
Throwing in an answer using Tally Table and mostly reliable ISNUMERIC SQL function
This should be good for small datasets. I also assume that you have valid numbers i.e. hour part are not >24, minute part or seconds part are not >60
create table #t(duration nvarchar(max));
insert into #t values
('PT10M13S')
,('PT13M22S')
,('PT1H2M18S')
,('PT11S')
select
totalseconds= sum(m.factor* case when ISNUMERIC(substring(duration, r-2,2))=1 then substring(duration, r-2,2) else substring(duration, r-1,1) end ),
duration from #t
cross join
(
select r=row_number() over (order by (select NULL))-1
from sys.objects s1 cross join sys.objects s2
)t
join
(values('S',1),('M',60),('H',3600)) m(part,factor)
on r<=len(duration) and substring(duration, r,1) =m.part
group by duration
drop table #t
PS: See this SO link which suggests that scalar UDF are faster than ISNUMERIC
Fastest way to check if a character is a digit?

Replacing Part of a String without Replace/Stuff

This is my string: 11.0000.0101.000.000.0101.000.000
I need to replace ONLY the first "0101" with "101." I can't use replace, as it replaces the 2nd instance of 0101 as well.
I tried
stuff(string, 9, 3, '101')
but since the replacement string is shorter than the existing string, I end up with
11.0000.1011.000.000.0101.000.000
What can I use besides REPLACE or STUFF? Thanks!
declare #t table(s varchar(100))
insert into #t values
( '11.0000.0101.000.000.0101.000.000'), ('abc');
select case charindex('0101', s)
when 0 then s
else stuff(s, charindex('0101', s), 4, '101')
end as new_s
from #t;
Your expression was almost right.
Just tell it to replace 4 characters of the original string instead of 3 :
stuff(string, 9, 4, '101')
But this will only work if your string has always the same positions.
you can do something like:
replace (stuff(string,9,3,'#101'),'#','')
If you need replace all 0101 to 101 go with below code
DECLARE #TempData TABLE(Data VARCHAR(1000));
INSERT INTO #TempData VALUES
('11.0000.0101.000.000.0101.000.000');
;WITH Cte
AS (
SELECT CASE
WHEN DATA = '0101'
THEN '101'
ELSE CAST(DATA AS VARCHAR(10))
END AS DATA
FROM (
SELECT Split.a.value('.', 'VARCHAR(1000)') AS Data
FROM (
SELECT CAST('<S>' + REPLACE(Data, '.', '</S><S>') + '</S>' AS XML) AS Data
FROM #TempData
) AS A
CROSS APPLY Data.nodes('/S') AS Split(a)
) Dt
)
SELECT STUFF((
SELECT '.' + DATA
FROM cte
FOR XML PATH('')
), 1, 1, '') AS ExpectedResult
OutPut
ExpectedResult
11.0000.101.000.000.101.000.000
Yet another option if you don't know the 1st observation
Declare #S varchar(50) = '11.0000.0101.000.000.0101.000.000'
Declare #Find varchar(25) = '0101'
Declare #Repl varchar(25) = '101'
Select isnull(stuff(#S, charindex(#Find,#S), len(#Find), #Repl),#S)
Returns
11.0000.101.000.000.0101.000.000

How to use regular expression to remove number in MS SQL Server Management Studio?

I have a field in a table containing different IDs for different programmes like this:
ProgrammeID
-----------
Prog201604L
Prog201503L
Pro2015N
Pro2014N
Programme2010
Programme2011
Each programme ID has its meaning. The number in the mid of the string indicates the time or month. It is obvious that Prog201604L and Prog201503L indicate the same programme but in different years (so do the rest). What I want to do is to remove the numbers so after removal the programmeID will be like:
ProgrammeID
-----------
ProgL
ProgL
ProN
ProN
Programme
Programme
Then later I can aggregate this programmes together.
I am currently using SSMS 2012 not sure if there is a sql statement like RegEx. I have been searching for a long time but the solution online are mainly about Oracle and MySQL. What I found is PATINDEX() and it seems to support regular expression. Can anybody tell me how to create a pattern that suits my situation and what kind of statement I should use?
Thanks in advance
If the Number part is always 6 characters below can be used.
DECLARE #ProgrammeID VARCHAR(50) = 'Prog201604L'
SELECT STUFF(#ProgrammeID, PATINDEX( '%[0-9]%', #ProgrammeID), 6, '')
If the numbers are not fixed... to extend above
CREATE TABLE #Programme ( ProgrammeID VARCHAR(50) )
INSERT INTO #Programme
VALUES
('Prog201604L')
,('Pro2015N')
,('Programme2010')
,('Prog2016L')
,('Pro2N')
,('Prog')
,('2010')
SELECT ProgrammeID,
ISNULL(
STUFF(ProgrammeID,
PATINDEX( '%[0-9]%', ProgrammeID), -- get number start index
IIF(PATINDEX( '%[0-9][a-z]%',ProgrammeID)= 0, PATINDEX( '%[0-9]',ProgrammeID), PATINDEX( '%[0-9][a-z]%',ProgrammeID)) + 1 -- get the last number index
- PATINDEX( '%[0-9]%', ProgrammeID), -- get the number character length
'')
,ProgrammeID) -- Where there are no numbers in the string you will get Null, replace it with actual string
AS [Without Numbers]
FROM #Programme
this will handle cases with varying numbers and even string without number.
Hope this helps
You can create a function and pass the value of each row to function
as (just run this query)
Create Function [dbo].[RemoveNonAlphaCharacters](#Temp VarChar(1000))
Returns VarChar(1000)
AS
Begin
Declare #KeepValues as varchar(50)
Set #KeepValues = '%[^a-z]%'
While PatIndex(#KeepValues, #Temp) > 0
Set #Temp = Stuff(#Temp, PatIndex(#KeepValues, #Temp), 1, '')
Return #Temp
End
---Call it like this:
Declare #tbl table (ProgrammeID varchar(20))
insert into #tbl values ('ProgL'),('ProgL'),('ProN'),('ProN'),('Programme'),('Programme')
select * from #tbl
Select dbo.RemoveNonAlphaCharacters(ProgrammeID) from #tbl
How to strip all non-alphabetic characters from string in SQL Server?
Remove numbers from string sql server
One clever option is to take the substring of the ProgrammeID column from the left, until hitting the first number, and concatenate that with the reverse of the substring from the right until hitting the first number:
SELECT
SUBSTRING(ProgrammeID,
1,
PATINDEX('%[0-9]%', ProgrammeID) - 1) +
REVERSE(SUBSTRING(REVERSE(ProgrammeID),
1,
PATINDEX('%[0-9]%', REVERSE(ProgrammeID)) - 1))
FROM yourTable
I have created a user-defined function for SQL Server to remove non-numeric characters in a string expression
We can modify it to remove the opposite, numeric characters from the input string as follows
while patindex('%[0-9]%', #str) > 0
set #str = stuff(#str, patindex('%[0-9]%', #str), 1, '')
return #str
I hope it helps
Alan Burstein wrote an iTVF exactly for this. The function is called PatExclude8K. Here is the function definition (some comments removed):
CREATE FUNCTION dbo.PatExclude8K
(
#String VARCHAR(8000),
#Pattern VARCHAR(50)
)
/*******************************************************************************
Purpose:
Given a string (#String) and a pattern (#Pattern) of characters to remove,
remove the patterned characters from the string.
*******************************************************************************/
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
WITH
E1(N) AS (SELECT N FROM (VALUES (NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)) AS X(N)),
itally(N) AS
(
SELECT TOP(CONVERT(INT,LEN(#String),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM E1 T1 CROSS JOIN E1 T2 CROSS JOIN E1 T3 CROSS JOIN E1 T4
)
SELECT NewString =
((
SELECT SUBSTRING(#String,N,1)
FROM iTally
WHERE 0 = PATINDEX(#Pattern,SUBSTRING(#String COLLATE Latin1_General_BIN,N,1))
FOR XML PATH(''),TYPE
).value('.[1]','varchar(8000)'));
GO
And here is how you would use it:
SELECT *
FROM #Programme p
CROSS APPLY dbo.PatExclude8K(p.ProgrammeID, '[0-9]');
Using your sample data, here is the result:
ProgrammeID NewString
-------------------- -----------------
Prog201604L ProgL
Prog201503L ProgL
Pro2015N ProN
Pro2014N ProN
Programme2010 Programme
Programme2011 Programme
I created this solution building on a solution to extracting values from a comma separated list inside a string.
It seems to work find and even be a bit more effective than using while - I will be happy for feedback about that assumption, though.
On on table with 461.358 rows it takes 3 minutes and 27 seconds to do this (0.44 ms per row) (I put it into a function).
select count(*)
from Mytable
where dbo.StripNumeric(inputFromUser) is null
Here's the solutions
For stripping away numeric:
declare #input nvarchar(max) = null
select #input = '1a2 3b4' + char(13) + char(10) + '5(678)*90c'
DECLARE #output nvarchar(max) = '';
WITH cte AS
(
SELECT cast(1 as int) as [index]
UNION ALL
SELECT [index]+ 1 as [index]
from cte
where [index] < len(#input)
)
select #output = iif(PATINDEX('%[0-9]%', substring(#input, [index], 1))= 1, #output, #output + substring(#input, [index], 1))
from cte;
select iif(COALESCE( #output, '') = '', null, ltrim(rtrim(#output)))
For stripping away non-numeric:
declare #input nvarchar(max) = null
select #input = '1a2 3b4' + char(13) + char(10) + '5(678)*90c'
DECLARE #output nvarchar(max) = '';
WITH cte AS
(
SELECT cast(1 as int) as [index]
UNION ALL
SELECT [index]+ 1 as [index]
from cte
where [index] < len(#input) --len(substring(#input, index, 1)) >
)
select #output = iif(PATINDEX('%[0-9]%', substring(#input, [index], 1))= 1, #output + substring(#input, [index], 1), #output)
from cte;
select iif(COALESCE( #output, '') = '', null, ltrim(rtrim(#output)))

Convert varchar value to date in SQL Server

I'm converting varchar data to date in SQL server.
Table having data like below,
So it can have NULL value, proper formatted date and can have like 19900411and 04221995.
So I have tried something like below, but getting error.
SELECT CASE
WHEN ISNULL(CAST(Dob AS VARCHAR), '') = '' THEN NULL
WHEN LEN(CAST(Dob AS VARCHAR)) = '8' THEN CONVERT(
VARCHAR(10),
CONVERT(date, RIGHT(Dob, 4) + LEFT(Dob, 2) + SUBSTRING(Dob, 3, 2)),
103
)
ELSE CONVERT(VARCHAR, CAST(Dob AS CHAR(8)), 103)
END
FROM TableName
WHERE Dob IS NOT NULL
Msg 241, Level 16, State 1, Line 3 Conversion failed when converting
date and/or time from character string.
I wanted to get output as date format MM-dd-yyyy
Please help me! Thanks!
Can you check this answer. This is work for Sqlserver 2012 and above.
Based on your discussion I created a sample data.
CREATE TABLE #A
( COL VARCHAR(10)
)
INSERT INTO #A VALUES( NULL),('19900411'),('19900411'),('04-04-1976'),('10-30-1952')
insert into #A values ('04221995')
insert into #A values ('02222009 ')
select * from #a
SELECT
case isdate(col ) when 1 then CONVERT(VARCHAR, CONVERT(DATE, COL) , 105)
when 0 then SUBSTRING(COL,1,2) + '-'+SUBSTRING(COL,3,2) + '-'+SUBSTRING(COL,5,4)
end
FROM #A
You can use this query for your problem. It simply convert your varchar column to formatted MM-dd-yyyy
select convert(varchar(10), cast('2011-09-28' as date), 101)
I think, you need this:
SELECT CASE
WHEN ISNULL(CAST(Dob AS VARCHAR), '') = '' THEN NULL
WHEN LEN(CAST(Dob AS VARCHAR)) = '8' THEN CONVERT(VARCHAR(10),
CONVERT(date, RIGHT(Dob, 2) + '-' + SUBSTRING(Dob, 5, 2) +'-' + LEFT(Dob, 4)),
103)
ELSE CONVERT(VARCHAR, CAST(Dob AS CHAR(10)), 103)
END
FROM tbl
WHERE Dob IS NOT NULL
OUTPUT: 04/11/1990
SQL Fiddle DEMO: SQL DEMO
CREATE TABLE #A
( COL VARCHAR(10)
)
INSERT INTO #A VALUES( NULL),('19900411'),('19900411'),('04-04-1976'),('10-30-1952'),('19950422')
SELECT DATESTRINGFIELD = CONVERT(VARCHAR, CONVERT(DATE, COL) , 101) FROM #A
output
04/11/1990
04/11/1990
04/04/1976
10/30/1952
04/22/1995

Resources