There has been questions related to this asked, but I have not gotten the solution I am need of.
The string(s) I am trying to split up look like this:
/Dev/act/billing
or
/ST/recManage/prod/form
The issue I have is the first '/' giving me problems when I try and do things with LEFT/RIGHT/SUBSTRING/CHARINDEX. It messes up counts and stopping at the delimiter. Also, it is important to note that the number of delimiters changes. So I want to find a way to split it up so I can get every possible substring.
RIGHT(c3.Path,CHARINDEX('/', REVERSE(c3.Path))-1) AS LastPath
This has gotten me the last part of the string. I have messed with other things ilke:
SUBSTRING(c3.Path,CHARINDEX('/',c3.Path,(CHARINDEX('/',c3.Path)+1))+1,len(c3.Path)),
This gets everything after the second '/'
I have also messed with XML and
SET #delimiter='/'
;WITH CTE AS
(SELECT CAST('<M>' + REPLACE([Path], #delimiter , '</M><M>') + '</M>' AS XML)
AS [Type XML]
FROM [Rpts].[dbo].[Cata]
)
,
CTE2 as (Select [Type XML].value('/M[2]', 'varchar(50)') As FirstPath from CTE)
Then doing: CTE2.FirstPath to get the result. But this then gives NULL
I am not on SQL 2016 so I cannot use SPLIT_STRING.
Thank you
Try this:
DECLARE #mockup TABLE(ID INT IDENTITY,YourString VARCHAR(MAX));
INSERT INTO #mockup VALUES('/Dev/act/billing'),('/ST/recManage/prod/form');
SELECT m.ID
,B.part.value(N'text()[1]',N'nvarchar(max)')
FROM #mockup AS m
OUTER APPLY(SELECT CAST('<x>' + REPLACE(m.YourString,'/','</x><x>') + '</x>' AS XML)) AS A(Casted)
OUTER APPLY A.Casted.nodes(N'/x[text()]') AS B(part);
This approach is save as long as you do not have forbidden characters in your string (namely <, > and &). If you need this, just call in, it is possible to solve.
Using .nodes() with the XQuery predicat [text()] will ommit all rows with no value...
The result
ID Part
---------
1 Dev
1 act
1 billing
2 ST
2 recManage
2 prod
2 form
Without going dynamic, and if you have a limited (or max) number of columns, perhaps something like this:
The replace() in the Cross Apply assumes the strings begins with a '/'
(Easy to expand or contract ... the pattern is pretty clear)
Example
Declare #YourTable table (ID int,[Path] varchar(max))
Insert Into #YourTable values
(1,'/Dev/act/billing')
,(2,'/ST/recManage/prod/form')
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select Pos1 = xDim.value('/x[1]','varchar(max)')
,Pos2 = xDim.value('/x[2]','varchar(max)')
,Pos3 = xDim.value('/x[3]','varchar(max)')
,Pos4 = xDim.value('/x[4]','varchar(max)')
,Pos5 = xDim.value('/x[5]','varchar(max)')
,Pos6 = xDim.value('/x[6]','varchar(max)')
,Pos7 = xDim.value('/x[7]','varchar(max)')
,Pos8 = xDim.value('/x[8]','varchar(max)')
,Pos9 = xDim.value('/x[9]','varchar(max)')
From (Select Cast('<x>' + replace(substring(A.Path,2,len(A.Path)),'/','</x><x>')+'</x>' as xml) as xDim) as A
) B
Returns
EDIT - Slightly Simplified Version
Notice the staggard Pos1 and /x[2]
Select A.ID
,Pos1 = xDim.value('/x[2]','varchar(max)')
,Pos2 = xDim.value('/x[3]','varchar(max)')
,Pos3 = xDim.value('/x[4]','varchar(max)')
,Pos4 = xDim.value('/x[5]','varchar(max)')
,Pos5 = xDim.value('/x[6]','varchar(max)')
From #YourTable A
Cross Apply ( Select Cast('<x>' + replace(A.Path,'/','</x><x>')+'</x>' as xml) ) B (xDim)
Related
I have a bunch of rows that have a field called "name" that have values like this, delimited by a _:
ServerNumber_BrandNumber_BrandName_JobName
Sometimes, the job name will be spread out over two deliminations, like this:
ServerNumber_BrandNumber_BrandName_JobName_JobNamePart2
I want to break out each of those into their own field in a select statement like this:
SELECT
name[0] as ServerNumber,
name[1] as BrandNumber,
name[2] as BrandName,
name[3] as JobName
from table
If I do something like this it will work if job name is only part of one delmiter, but it will return nothing if it's using two:
REVERSE(PARSENAME(REPLACE(REVERSE(name), '_', '.'), 1))
How can I do all of this?
Working example
Declare #YourTable Table ([Name] varchar(150)) Insert Into #YourTable Values
('ServerNumber_BrandNumber_BrandName_JobName')
,('ServerNumber_BrandNumber_BrandName_JobName_JobNamePart2')
Select Pos1 = JSON_VALUE(JS,'$[0]')
,Pos2 = JSON_VALUE(JS,'$[1]')
,Pos3 = JSON_VALUE(JS,'$[2]')
,Pos4 = concat(JSON_VALUE(JS,'$[3]'),'_'+JSON_VALUE(JS,'$[4]'))
From #YourTable A
Cross Apply (values ('["'+replace(replace(string_escape([Name],'json'),' ','_'),'_','","')+'"]') ) B(JS)
Results
Pos1 Pos2 Pos3 Pos4
ServerNumber BrandNumber BrandName JobName
ServerNumber BrandNumber BrandName JobName_JobNamePart2
XML Approach (2012)
Select Pos1 = xDim.value('/x[1]','varchar(150)')
,Pos2 = xDim.value('/x[2]','varchar(150)')
,Pos3 = xDim.value('/x[3]','varchar(150)')
,Pos4 = concat(xDim.value('/x[4]','varchar(150)'),'_'+xDim.value('/x[5]','varchar(150)'))
From #YourTable A
Cross Apply ( values (cast('<x>' + replace((Select replace(NAME,'_','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml))) B(xDim)
I have an AWS server name and I am trying to just get the domain name.
Here is an example of the server name:
SERVER1.SERVER2.US-WEST-5.RDS.AMAZONAWS.COM,59642
I just want to return:
US-WEST-5.RDS.AMAZONAWS.COM
I have tried using something similar to this:
But that is only for semi-colons. I know it is nested substrings and charindexes,
You can try this using SUBSTRING (Transact-SQL)
.
Returns part of a character, binary, text, or image expression in SQL Server.
Syntax
SUBSTRING ( expression ,start , length )
Here is the implementation.
SELECT SUBSTRING('SERVER1.SERVER2.US-WEST-5.RDS.AMAZONAWS.COM,59642',
1, CHARINDEX(',', 'SERVER1.SERVER2.US-WEST-5.RDS.AMAZONAWS.COM,59642') - 1)
AS FirstPart
Here is the live db<>fiddle demo.
Just another option. Perhaps a little overkill, but it demonstrates how you can parse a delimited string into columns.
Example
Declare #YourTable table (ID int,SomeCol varchar(500))
Insert Into #YourTable values
(1,'SERVER1.SERVER2.US-WEST-5.RDS.AMAZONAWS.COM,59642')
,(2,'SERVER1.SERVER2.US-WEST-6.RDS.AMAZONAWS.COM') -- note no trailing ,
,(2,'SERVER1.SERVER2.US-WEST-6.RDS.DEV.AMAZONAWS.COM') -- note extra DEV ,
Select A.ID
,NewStr = concat(pos3,'.'+pos4,'.'+pos5,'.'+pos6,'.'+pos7,'.'+pos8,'.'+pos9)
From #YourTable A
Cross Apply ( values ( left(SomeCol,charindex(',',SomeCol+',')-1 ))) B(CleanString) -- Removes Trailing ,####
Cross Apply ( -- Parses CleanString on .
Select Pos1 = xDim.value('/x[1]','varchar(max)') -- Can be removed
,Pos2 = xDim.value('/x[2]','varchar(max)') -- Can be removed
,Pos3 = xDim.value('/x[3]','varchar(max)')
,Pos4 = xDim.value('/x[4]','varchar(max)')
,Pos5 = xDim.value('/x[5]','varchar(max)')
,Pos6 = xDim.value('/x[6]','varchar(max)')
,Pos7 = xDim.value('/x[7]','varchar(max)')
,Pos8 = xDim.value('/x[8]','varchar(max)')
,Pos9 = xDim.value('/x[9]','varchar(max)')
From ( values (cast('<x>' + replace(CleanString,'.','</x><x>')+'</x>' as xml))) as A(xDim)
) C
Returns
ID NewStr
1 US-WEST-5.RDS.AMAZONAWS.COM
2 US-WEST-6.RDS.AMAZONAWS.COM
2 US-WEST-6.RDS.DEV.AMAZONAWS.COM
I am trying to split a string in SQL on the space delimiter. The number of spaces changes, so there can be values for 4 or 5 columns. I am trying using CHARINDEX and SUBSTRING, but can't get past the first delimiter.
Example:
input would be '444 Addison Avenue SA 5222'
To split into columns, you could use a little XML in concert with a CROSS APPLY
Example
Declare #YourTable Table ([SomeCol] varchar(50))
Insert Into #YourTable Values
('444 Addison Avenue SA 5222')
,('1 Washington Square')
Select A.*
,B.*
From #YourTable A
Cross Apply (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
From (Select Cast('<x>' + replace((Select replace([SomeCol],' ','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A
) B
Returns
You can achieve the same result only with XML-based splitting:
DECLARE #tmp TABLE ([Address] varchar(50))
INSERT INTO #tmp VALUES ('444 Addison Avenue SA 5222'),('1 Washington Square')
SELECT T.[Address]
,AddressElements.value(N'/x[1]','varchar(max)') AS Element_1
,AddressElements.value(N'/x[2]','varchar(max)') AS Element_2
,AddressElements.value(N'/x[3]','varchar(max)') AS Element_3
,AddressElements.value(N'/x[4]','varchar(max)') AS Element_4
,AddressElements.value(N'/x[5]','varchar(max)') AS Element_5
FROM (
SELECT [Address]
,CAST('<x>' + REPLACE([Address],' ','</x><x>') + '</x>' AS XML) AS AddressElements
FROM #tmp
) T
Results:
I am writing up a report where I have a varchar(max) column that has data in it separated by a ";" semicolon. The end users are asking for the report to break apart this one column and return it as a series of columns.
I am not sure how to do this.
The data has variable lengths based on the status of the customer. Some of the columns only have 60 characters in it, some have 400+ characters.
The data looks somewhat like this:
Result 1 = aaaaaaaa; bbbbbbbbbbbb; ccccccccccccccccccccccccc; ddddddddddddd;
Result 2 = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; bbbbbbbbb;
Result 3 = aaaaaaaaaaaaaa; bbbbbbbbbbbbbbb; ccccccccccccccccccccccccc;
The flip side of this is that if I were to break each semicolon section out, I could end up with 40 - 50 columns worth of data, while others might end up being just 3. (if that makes sense)
Everything else in the report is a piece of cake, it is just the splitting of this one column that has me questioning the whole endeavor.
I guess my question here is:
How would I break apart this one column into multiple based off the Semicolon delimiter?
With the helps of a CROSS APPLY and an little XML. As you can see, the XML portion is easy to expand or contract as necessary.
Example
Declare #YourTable table (ID int, SomeCol varchar(max))
Insert into #YourTable values
(1,'aaaaaaaa; bbbbbbbbbbbb; ccccccccccccccccccccccccc; ddddddddddddd;'),
(2,'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; bbbbbbbbb;'),
(3,'aaaaaaaaaaaaaa; bbbbbbbbbbbbbbb; ccccccccccccccccccccccccc;')
Select A.ID
,B.*
From #YourTable A
Cross Apply (
Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
From (Select Cast('<x>' + replace((Select replace(A.SomeCol,';','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A
) B
Returns
R/005/2016-17
This varchar value i have in a table, i need to add 1 to 005.
i need output R/006/2016-17 for the next entry
How can i split the string and add one.
Your table demonstrates just how bad things can be when you don't properly normalize your data. But you can do this using some string manipulation. I would strongly urge you to split this into the appropriate columns instead of keeping this all crammed together.
This code will produce the desired output based on your sample data.
declare #Something varchar(20) = 'R/005/2016-17'
select parsename(replace(#Something, '/', '.'), 3) + '/' + right('000' + convert(varchar(3), convert(int, parsename(replace(#Something, '/', '.'), 2)) + 1), 3) + '/' + parsename(replace(#Something, '/', '.'), 1)
If you don't have ParseName(), I have a TVF which may help. If you can't use the UDF, the logic is easily ported into the CROSS APPLY
Declare #YourTable table (ID int,String varchar(max))
Insert Into #YourTable values
(1,'R/005/2016-17'),
(2,'A/119/2016-18')
Update #YourTable
Set String = Pos1+'/'+right('000000'+cast(cast(Pos2 as int)+1 as varchar(25)),len(Pos2))+'/'+Pos3
From #YourTable A
Cross Apply [dbo].[udf-Str-Parse-Row](A.String,'/') B
Select * from #YourTable
The Update Table
ID String
1 R/006/2016-17
2 A/120/2016-18
The UDF if Needed
CREATE FUNCTION [dbo].[udf-Str-Parse-Row] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select Pos1 = xDim.value('/x[1]','varchar(max)')
,Pos2 = xDim.value('/x[2]','varchar(max)')
,Pos3 = xDim.value('/x[3]','varchar(max)')
,Pos4 = xDim.value('/x[4]','varchar(max)')
,Pos5 = xDim.value('/x[5]','varchar(max)')
,Pos6 = xDim.value('/x[6]','varchar(max)')
,Pos7 = xDim.value('/x[7]','varchar(max)')
,Pos8 = xDim.value('/x[8]','varchar(max)')
,Pos9 = xDim.value('/x[9]','varchar(max)')
From (Select Cast('<x>' + Replace(#String,#Delimiter,'</x><x>')+'</x>' as XML) as xDim) A
)
--Select * from [dbo].[udf-Str-Parse-Row]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse-Row]('John Cappelletti',' ')
You can use Computed Columns when create table for this type case.
Eg:-
[EmployeeNo] AS ([PreFix]+ RIGHT('0000000' + CAST(Id AS VARCHAR(7)), 7)) PERSISTED,
https://msdn.microsoft.com/en-us/library/ms188300.aspx