SQL replace/ remove multiple date and time stamp from any part of a string - sql

I have a nvarchar field that contains multiple date & time stamps and various text. The date and time can be at any position in the field.
I want to select only the text from the field. I have tried with REPLACE and PATINDEX to no avail.
Please can anyone share how i would write my select on this example notes field which contains this string:
ADMIN1 21/04/2017 02:01:01 This student is here and trying to gain a masters.
ITSYS2 09/05/2017 03:51:04 60 APL Credits on xout

The following will exclude dates and times from the note_detail. This is an in-line approach, but just about any split/parse function will do the trick as well.
Example
Declare #YourTable table(studend_id int,note_detail varchar(max))
Insert Into #YourTable values
(1,'CHIDLOL 21/04/2017 02:01:01 '+CHAR(13)+CHAR(10)+'This studend is here and trying to gain a masters. THOMASXC 09/05/2014 03:54:04 60 APL Credon on xout')
Select A.studend_id
,new_note_detail = B.S
From #YourTable A
Cross Apply (
Select S = Stuff((Select ' ' +RetVal
From (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(replace(replace(A.note_detail,char(13),' '),char(10),' '),' ','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) B1
Where RetVal not like '%[0-9]/[0-9][0-9]/[0-9]%'
and RetVal not like '%[0-9]:[0-9][0-9]:[0-9]%'
Order by RetSeq
For XML Path ('')),1,1,'')
) B
Returns
studend_id new_note_detail
1 CHIDLOL This studend is here and trying to gain a masters. THOMASXC 60 APL Credon on xout
Edit - Option 2 with a Parse Function
Select A.studend_id
,new_note_detail = B.S
From #YourTable A
Cross Apply (
Select S = Stuff((Select ' ' +RetVal
From [dbo].[udf-Str-Parse](replace(replace(A.note_detail,char(13),' '),char(10),' '),' ') B1
Where RetVal not like '%[0-9]/[0-9][0-9]/[0-9]%'
and RetVal not like '%[0-9]:[0-9][0-9]:[0-9]%'
Order by RetSeq
For XML Path ('')),1,1,'')
) B
The UDF if Interested
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
--Select * from [dbo].[udf-Str-Parse]('this,is,<test>,for,< & >',',')

Related

Select string in between two strings

I need to output a string in between two strings. The problem is sometimes one of the two reference strings will be missing. If the first reference string is not missing and the second reference string is missing, I want to output from the first reference string to the end of the string. If the first reference string is missing, I want to output null or blank.
I saw a similar post but it included the reference strings. In my case, I do not want to include the reference strings.
SELECT SUBSTRING(#Text, CHARINDEX('1stRefStr', #Text)
, CHARINDEX('2ndRefStr',#text) - CHARINDEX('1stRefStr', #Text) + Len('2ndRefStr'))
Example:
Patient: A Date: 1/1/1 Message: Hi Message Sent To: B
1st string reference is "Message:"
2nd string reference is "Message Sent To:"
Expected Result:
Hi
If you don't mind a helper function.
Being a TVF, it is easy to incorporate into a CROSS APPLY if your data is in a table.
I modified a split/parse function to accept two non-like delimeters.
Example
Declare #Text varchar(max) = 'Patient: A Date: 1/1/1 Message: Hi Message Sent To: B'
Select *
From [dbo].[tvf-Str-Extract](#Text,'Message:','Message Sent') A
Returns
RetSeq RetVal
1 Hi
The Function
CREATE FUNCTION [dbo].[tvf-Str-Extract] (#String varchar(max),#Delim1 varchar(100),#Delim2 varchar(100))
Returns Table
As
Return (
Select RetSeq = row_number() over (order by RetSeq)
,RetVal = left(RetVal,charindex(#Delim2,RetVal)-1)
From (
Select RetSeq = row_number() over (order by 1/0)
,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From ( values (convert(xml,'<x>' + replace((Select replace(#String,#Delim1,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>').query('.'))) as A(XMLData)
Cross Apply XMLData.nodes('x') AS B(i)
) C1
Where charindex(#Delim2,RetVal)>1
)
Update As A CROSS APPLY
Declare #YourTable table (ID int,SomeCol varchar(max))
Insert Into #YourTable values
(1,'Patient: A Date: 1/1/1 Message: Hi Message Sent To: B')
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select RetSeq = row_number() over (order by RetSeq)
,RetVal = left(RetVal,charindex('Message Sent',RetVal)-1)
From (
Select RetSeq = row_number() over (order by 1/0)
,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From ( values (convert(xml,'<x>' + replace((Select replace(SomeCol,'Message:','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>').query('.'))) as A(XMLData)
Cross Apply XMLData.nodes('x') AS B(i)
) C1
Where charindex('Message Sent',RetVal)>1
) B

Is it possible to compare comma delimited string in T-SQL without looping?

Let's say I have 2 tables where both has column called Brand. The value is comma delimited so for example if one of the table has
ACER,ASUS,HP
AMD,NVIDIA,SONY
as value. Then the other table has
HP,GIGABYTE
MICROSOFT
SAMSUNG,PHILIPS
as values.
I want to compare these table to get all matched record, in my example ACER,ASUS,HP and HP,GIGABYTE match because both has HP. Right now I'm using loop to achieve this, I'm wondering if it's possible to do this in a single query syntax.
You are correct in wanting to step away from the loop.
Since you are on 2012, String_Split() is off the table. However, there are any number of split/parse TVF functions in-the-wild.
Example 1 - without a TVF
Declare #T1 table (Brand varchar(50))
Insert Into #T1 values
('ACER,ASUS,HP'),
('AMD,NVIDIA,SONY')
Declare #T2 table (Brand varchar(50))
Insert Into #T2 values
('HP,GIGABYTE'),
('MICROSOFT'),
('SAMSUNG,PHILIPS')
Select Distinct
T1_Brand = A.Brand
,T2_Brand = B.Brand
From (
Select Brand,B.*
From #T1
Cross Apply (
Select RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace(Brand,',','</x><x>')+'</x>' as xml)) as A
Cross Apply x.nodes('x') AS B(i)
) B
) A
Join (
Select Brand,B.*
From #T2
Cross Apply (
Select RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace(Brand,',','</x><x>')+'</x>' as xml)) as A
Cross Apply x.nodes('x') AS B(i)
) B
) B
on A.RetVal=B.RetVal
Example 2 - with a TVF
Select Distinct
T1_Brand = A.Brand
,T2_Brand = B.Brand
From (
Select Brand,B.*
From #T1
Cross Apply [dbo].[tvf-Str-Parse](Brand,',') B
) A
Join (
Select Brand,B.*
From #T2
Cross Apply [dbo].[tvf-Str-Parse](Brand,',') B
) B
on A.RetVal=B.RetVal
Both Would Return
T1_Brand T2_Brand
ACER,ASUS,HP HP,GIGABYTE
The UDF if interested
CREATE FUNCTION [dbo].[tvf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[tvf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[tvf-Str-Parse]('John Cappelletti was here',' ')
--Select * from [dbo].[tvf-Str-Parse]('this,is,<test>,for,< & >',',')
Had the same problem with comparing "," delimited strings
you can use "XML" to do that and compare the outputs and return the same/different value:
declare #TestInput nvarchar(255)
, #TestInput2 nvarchar(255)
set #TestInput = 'ACER,ASUS,HP'
set #TestInput2 = 'HP,GIGABYTE'
;WITH FirstStringSplit(S1) AS
(
SELECT CAST('<x>' + REPLACE(#TestInput,',','</x><x>') + '</x>' AS XML)
)
,SecondStringSplit(S2) AS
(
SELECT CAST('<x>' + REPLACE(#TestInput2,',','</x><x>') + '</x>' AS XML)
)
SELECT STUFF(
(
SELECT ',' + part1.value('.','nvarchar(max)')
FROM FirstStringSplit
CROSS APPLY S1.nodes('/x') AS A(part1)
WHERE part1.value('.','nvarchar(max)') IN(SELECT B.part2.value('.','nvarchar(max)')
FROM SecondStringSplit
CROSS APPLY S2.nodes('/x') AS B(part2)
)
FOR XML PATH('')
),1,1,'') as [Same Value]
Edit:
Changed 'Stuff' to 'XML'

Break multiple values from lines

I have a select that gives me a result like:
A, B, C
C, D, E
G, A
NULL
O, U, B, X
I would like to have the result like:
A
B
C
D
E
G
O
U
X
So, instead of multiple values from lines, each value should be in 1 line with DISTINCT and without NULL
Tried to use SQLFIDDLE to create an example but it is not working.
Everyone should have a good split/parse function.
Option 1 without a UDF
Declare #YourTable table (SomeCol varchar(100))
Insert Into #YourTable values
('A, B, C'),
('C, D, E'),
('G, A'),
(NULL),
('O, U, B, X')
Select Distinct B.RetVal
From #YourTable A
Cross Apply (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(A.SomeCol,',','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as X
Cross Apply x.nodes('x') AS B(i)
) B
Where B.RetVal is not null
Option 2 With a Parse/Spit UDF
Select Distinct B.RetVal
From #YourTable A
Cross Apply [dbo].[udf-Str-Parse](A.SomeCol,',') B
Where B.RetVal is not null
Both Return
RetVal
A
B
C
D
E
G
O
U
X
The UDF if Interested
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as X
Cross Apply x.nodes('x') AS B(i)
);
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
--Select * from [dbo].[udf-Str-Parse]('this,is,<test>,for,< & >',',')
You can use cross apply to unpivot them. The rest is just select distinct:
select distinct v.val
from t cross apply
(values (col1), (col2), (col3)) val
where val is not null;

Parsing a SQL field in a query

I've inherited a database of user profile information which has a column for personal interests. Multiple interests are separated by a pipe (|). In a SQL query, how can I split a field with this value: 2|27|33|14|15
To look like this:
2
27
33
14
15
The exact syntax depends on which dbms you are using. Assuming you are using MSSQL this is the general syntax
STRING_SPLIT ( string , separator )
For example
DECLARE #string_to_be_split NVARCHAR(400) = '2|27|33|14|15'
SELECT value
FROM STRING_SPLIT(#string_to_be_split, '|')
WHERE RTRIM(value) <> '';
Edit - Could have sworn that I saw SQL Server
If not 2016, just about any Split/Parse Function will do.
Option 1 - With UDF
Declare #YourTable table (ID int,Interests varchar(250))
Insert Into #YourTable values
(1,'2|27|33|14|15')
Select A.ID
,B.*
From #YourTable A
Cross Apply [dbo].[udf-Str-Parse](A.Interests,'|') B
Option 2 - Without a UDF
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(A.Interests,'|','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as X
Cross Apply x.nodes('x') AS B(i)
) B
Both Return
ID RetSeq RetVal
1 1 2
1 2 27
1 3 33
1 4 14
1 5 15
The UDF if Interested
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as X
Cross Apply x.nodes('x') AS B(i)
);
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
--Select * from [dbo].[udf-Str-Parse]('this,is,<test>,for,< & >',',')

Line break or Carriage return in a Delimited Field in Sql

I have an email column that stores a minimum of more than 10 emails in a row. Now, I want to write a query that puts each email on a separate line, e.g:
hay#line.com
u#y.com
live.gmail.com
How do write this?
If you mean rows of data... Any Parse/Split function will do if you don't have 2016. Otherwise the REPLACE() as JohnHC mentioned
Declare #YourTable table (ID int,Emails varchar(max))
Insert Into #YourTable values
(1,'hay#line.com,u#y.com,live.gmail.com')
Select A.ID
,EMail=B.RetVal
From #YourTable A
Cross Apply [dbo].[udf-Str-Parse](A.EMails,',') B
Returns
ID EMail
1 hay#line.com
1 u#y.com
1 live.gmail.com
Or Simply
Select * from [dbo].[udf-Str-Parse]('hay#line.com,u#y.com,live.gmail.com',',')
Returns
RetSeq RetVal
1 hay#line.com
2 u#y.com
3 live.gmail.com
The Function if Needed
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>'+ Replace(#String,#Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
Use Replace()
select replace(MyEmailField, '<CurrentDelimeter>', char(13)) as NewEmail
from MyTable