I have found the following snippet online:
DECLARE #str VARCHAR(5000)= 'aaaa bbbbb cccccccc ddddddddddddddddddd'
SELECT TOP 1 Split.a.value('.', 'VARCHAR(100)') as longest_Word
FROM (SELECT Cast ('<M>' + Replace(#str, ' ', '</M><M>') + '</M>' AS XML) AS Data) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
ORDER BY Len(Split.a.value('.', 'VARCHAR(100)')) DESC
Which finds the longest word in a text file ('ddddddddddddddddddd in this case). However, I do not know how to apply this per row in a certain column so that it adds a column next to each row which tells me what the longest word is in SQL Server.
Each column in SQL Server in which it needs to search should look like this with the extra column added next to it:
COLUMN 1 RESULT
ABC ABCD ABCD
BC BCDE BCDE
9II IIIIV IIIIV
... ...
A simple CROSS APPLY can help here.
Note, I used my own XML Parser because it is "XML Safe"
Declare #YourTable table ([COLUMN 1] varchar(max))
Insert Into #YourTable values
('ABC ABCD'),
('BC BCDE'),
('9II IIIIV')
Select A.[COLUMN 1]
,Result = B.RetVal
From #YourTable A
Cross Apply (
Select Top 1 *
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(A.[COLUMN 1],' ','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) B1
Order by Len(RetVal) Desc,RetSeq
) B
Returns
COLUMN 1 Result
ABC ABCD ABCD
BC BCDE BCDE
9II IIIIV IIIIV
EDIT - You Want to Show TIES
Declare #YourTable table ([COLUMN 1] varchar(max))
Insert Into #YourTable values
('ABC ABCD'),
('BC BCDE 1234'),
('9II IIIIV')
Select A.[COLUMN 1]
,Result = B.RetVal
From #YourTable A
Cross Apply (
Select Top 1 with ties *
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(A.[COLUMN 1],' ','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) B1
Order by Dense_Rank() over (Order by Len(RetVal) Desc)
) B
Returns
COLUMN 1 Result
ABC ABCD ABCD
BC BCDE 1234 BCDE --<< Tie
BC BCDE 1234 1234 --<< Tie Added for Illustration
9II IIIIV IIIIV
Related
I have 2 strings
Declare #WhenDetails NVarchar(Max) ='07:00:0:0;1:00:1:0;6:00:1:0;10:00:1:0;'
Declare #Dosage NVarchar(Max) ='1.00;2.00;1.00;1.00'
I need to split these 2 string and insert into a table
Example at 07:00:0:0=>1.00 1:00:1:0=>2.00
Declare #TempDosageWhenDetails Table (RowID INT IDENTITY(1,1), PatientMedicationID INT, Dosage NVARCHAR(Max),WhenDetails NVARCHAR(Max))
insert #TempDosageWhenDetails(Dosage)
select x.items
from dbo.Split('07:00:0:0;1:00:1:0;6:00:1:0;10:00:1:0;', ';') x
I have taken a table and split and inserted my when details
How can fill the dosage column as shown in the example?
Note I might have n number of records to split I have given these just an example.
Perhaps with a little JSON (assuming 2016+)
Example
Declare #WhenDetails NVarchar(Max) ='07:00:0:0;1:00:1:0;6:00:1:0;10:00:1:0;'
Declare #Dosage NVarchar(Max) ='1.00;2.00;1.00;1.00'
Select RowID = A.[Key]+1
,PatientID = null
,Dosage = B.[Value]
,WhenDetails = A.[Value]
From (
Select *
From OpenJSON( '["'+replace(#WhenDetails,';','","')+'"]' )
) A
Join (
Select *
From OpenJSON( '["'+replace(#Dosage,';','","')+'"]' )
) B
on A.[Key]=B.[Key]
Returns
RowID PatientID Dosage WhenDetails
1 NULL 1.00 07:00:0:0
2 NULL 2.00 1:00:1:0
3 NULL 1.00 6:00:1:0
4 NULL 1.00 10:00:1:0
If it helps with the Visualization:
We convert the strings into a JSON array, then it is a small matter to join the results based on the KEY
If you were to
Select * From OpenJSON( '["'+replace(#WhenDetails,';','","')+'"]' )
The results would be
key value type
0 07:00:0:0 1
1 1:00:1:0 1
2 6:00:1:0 1
3 10:00:1:0 1
EDIT - XML Approach
Select RowID = A.RetSeq
,PatientID = null
,Dosage = B.RetVal
,WhenDetails = A.RetVal
From (
Select RetSeq = row_number() over (order by 1/0)
,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#WhenDetails,';','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) A
Join (
Select RetSeq = row_number() over (order by 1/0)
,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#Dosage,';','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) B
on A.RetSeq=B.RetSeq
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'
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,< & >',',')
I have a table which is created dynamically. So the number of columns is unknown at the time of creation. I want to create copies of each column in the same table with first column holding the first part of comma separated value, second column the second part and so on
For example,
ID Value1 Value2 .... Valuen
1 1;2;3 4;5;6
2 A;B;C D;E;F
I want to get the output like
ID Value1Copy1 Value1Copy2 Value1Copy3 Value2Copy1 Value2Copy2 Value2Copy3 .... ValuenCopy1
1 1 2 3 4 5 6
2 A B C D E F
I am unable to achieve this for variable number of columns
The following will dynamically unpivot your data. You may notice that the only field specified is ID.
The results are dropped into a #Temp table. From there we perform a dynamic pivot
Example
Declare #YourTable table (ID int,Value1 varchar(50),Value2 varchar(50))
Insert Into #YourTable values
( 1, '1;2;3','4;5;6'),
( 2, 'A;B;C','D;E;F')
Select A.ID
,Col = concat(C.Item,'Copy',D.RetSeq)
,Value = D.RetVal
Into #Temp
From #YourTable A --<< Replace with Your actual table
Cross Apply (Select XMLData = cast((Select A.* For XML Raw) as xml ) ) B
Cross Apply (
Select Item = attr.value('local-name(.)','varchar(100)')
,Value = attr.value('.','varchar(max)')
From B.XMLData.nodes('/row') as A(r)
Cross Apply A.r.nodes('./#*') AS B(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('ID','Other2Exclude')
) C
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(C.Value,';','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) D
Where A.ID is not null -- or any other WHERE statement
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName(Col) From #Temp Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select *
From #Temp
Pivot (max(Value) For [Col] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
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,< & >',',')