Input looks like
/nfs/site/disks/mtl_workdisk_003/mtlmedia/filter_bundle2846/regress/
/nfs/site/disks/mtl_workdisk_003/mtl3d/filter_bundle2846/regress/
/nfs/site/disks/mtl_workdisk_003/etl66/filter_bundle2846/regress/
Output should be
mtlmedia
mtl3d
etl66
I am able to successfully get the output but is there anyway to simplify it using some other functions or method ?
declare #T table
(
InputString varchar(max)
);
insert into #T values
('/nfs/site/ssd/mtl_workdisk_1/mediatek/filter_candle46/regression/'),
('/nfs/location/disks/mtl_workdisk_003/mtl3d/filter_candle2846/regress/'),
('/nfs/place/disks/mtl_workdisk_003/etl1266/bundle2846/regress/') ;
select -- P1.Pos, P2.Pos, P3.Pos,P4.Pos,P5.Pos,P6.Pos,
substring(InputString, P5.Pos + 1, P6.Pos - P5.Pos - 1) as OutputString
from #T
cross apply (select (charindex('/', InputString))) as P1(Pos)
cross apply (select (charindex('/', InputString, P1.Pos+1))) as P2(Pos)
cross apply (select (charindex('/', InputString, P2.Pos+1))) as P3(Pos)
cross apply (select (charindex('/', InputString, P3.Pos+1))) as P4(Pos)
cross apply (select (charindex('/', InputString, P4.Pos+1))) as P5(Pos)
cross apply (select (charindex('/', InputString, P5.Pos+1))) as P6(Pos) ;
Approaches, based on built-in XML or JSON support, are possible options:
Statement, based on XML:
SELECT CAST('<x>' + REPLACE(InputString, '/', '</x><x>') + '</x>' AS XML).value('/x[6]','varchar(max)')
FROM #t
Statement, based on JSON:
SELECT JSON_VALUE(CONCAT('["', REPLACE(InputString, '/', '","'), '"]'), '$[5]')
FROM #T
Result:
mediatek
mtl3d
etl1266
Notes:
JSON support is available from SQL Server 2016.
If you always know the root of the path and just want the first subfolder name after that, you could get a substring based on the length of the root combined with a charindex looking for the first slash after the root.
declare #paths as table(fullpath varchar(max))
insert into #paths values ('/nfs/site/disks/mtl_workdisk_003/mtlmedia/filter_bundle2846/regress/')
insert into #paths values ('/nfs/site/disks/mtl_workdisk_003/mtl3d/filter_bundle2846/regress/')
insert into #paths values ('/nfs/site/disks/mtl_workdisk_003/etl66/filter_bundle2846/regress/')
declare #root as varchar(max) = '/nfs/site/disks/mtl_workdisk_003/'
declare #startPos as int = len(#root) + 1
select substring(fullpath, #startPos, CHARINDEX('/',fullpath,#startPos) - #startPos) filename
from #paths
where CHARINDEX('/',fullpath,#startPos) > 0
I did the where clause at the end to prevent any errors if for some reason, there was no subfolder (slash) found after the root.
If you always know the ending of the path as well, you could just use a REPLACE to remove the ending and not even have to search for the "/" with the CHARINDEX.
Related
Heads!
In my database, I have a column that contains the following data (examples):
H-01-01-02-01
BLE-01-03-01
H-02-05-1.1-03
The task is to get the second to last element of the array if you would split that using the "-" character. The strings are of different length.
So this would be the result using the above mentioned data:
02
03
1.1
Basically I'm searching for an equivalent of the following ruby-statement for use in a Select-Statement in SQL-Server:
"BLE-01-03-01".split("-")[-2]
Is this possible in any way in SQL Server? After spending some time searching for a solution, I only found ones that work for the last or first element.
Thanks very much for any clues or solutions!
PS: Version of SQL Server is Microsoft SQL Server 2012
As an alternative you can try this:.
--A mockup table with some test data to simulate your issue
DECLARE #mockupTable TABLE (ID INT IDENTITY, YourColumn VARCHAR(50));
INSERT INTO #mockupTable VALUES
('H-01-01-02-01')
,('BLE-01-03-01')
,('H-02-05-1.1-03');
--The query
SELECT CastedToXml.value('/x[sql:column("CountOfFragments")-1][1]','nvarchar(10)') AS TheWantedFragment
FROM #mockupTable t
CROSS APPLY(SELECT CAST('<x>' + REPLACE(t.YourColumn,'-','</x><x>') + '</x>' AS XML))A(CastedToXml)
CROSS APPLY(SELECT CastedToXml.value('count(/x)','int')) B(CountOfFragments);
The idea in short:
The first APPLY will transform the string to a XML like this
<x>H</x>
<x>01</x>
<x>01</x>
<x>02</x>
<x>01</x>
The second APPLY will xquery into this XML to get the count of fragments. As APPLY will add this as a column to the result set, we can use the value using sql:column() to get the wanted fragment by its position.
As I wrote in my comment - using charindex with reverse.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
Col Varchar(100)
);
INSERT INTO #T (Col) VALUES
('H-01-01-02-01'),
('BLE-01-03-01'),
('H-02-05-1.1-03');
The query:
SELECT Col,
LEFT(RIGHT(Col, AlmostLastDelimiter-1), AlmostLastDelimiter - LastDelimiter - 1) As SecondToLast
FROM #T
CROSS APPLY (SELECT CharIndex('-', Reverse(Col)) As LastDelimiter) As A
CROSS APPLY (SELECT CharIndex('-', Reverse(Col), LastDelimiter+1) As AlmostLastDelimiter) As B
Results:
Col SecondToLast
H-01-01-02-01 02
BLE-01-03-01 03
H-02-05-1.1-03 1.1
Similar to Zohar's solution, but using CTEs instead of CROSS APPLY to prevent redundancy. I personally find this easier to follow, as you can see what happens in each step. Doesn't make it a better solution though ;)
DECLARE #strings TABLE (data VARCHAR(50));
INSERT INTO #strings VALUES ('H-01-01-02-01') , ('BLE-01-03-01'), ('H-02-05-1.1-03');
WITH rev AS (
SELECT
data,
REVERSE(data) AS reversed
FROM
#strings),
first_hyphen AS (
SELECT
data,
reversed,
CHARINDEX('-', reversed) + 1 AS first_pos
FROM
rev),
second_hyphen AS (
SELECT
data,
reversed,
first_pos,
CHARINDEX('-', reversed, first_pos) AS second_pos
FROM
first_hyphen)
SELECT
data,
REVERSE(SUBSTRING(reversed, first_pos, second_pos - first_pos)) AS result
FROM
second_hyphen;
Results:
data result
H-01-01-02-01 02
BLE-01-03-01 03
H-02-05-1.1-03 1.1
Try this
declare #input NVARCHAR(100)
declare #dlmt NVARCHAR(3);
declare #pos INT = 2
SET #input=REVERSE(N'H-02-05-1.1-03');
SET #dlmt=N'-';
SELECT
CAST(N'<x>'
+ REPLACE(
(SELECT REPLACE(#input,#dlmt,'#DLMT#') AS [*] FOR XML PATH(''))
,N'#DLMT#',N'</x><x>'
) + N'</x>' AS XML).value('/x[sql:variable("#pos")][1]','nvarchar(max)');
I have a table and the values like this
000001U;000002;000003U;000004;000005U;000006U
and I want display the field is like
000002;000004;
Try This
DECLARE #Table AS TABLE (Data nvarchar(1000))
INSERT INTO #Table
SELECT '000001U;000002;000003U;000004;000005U;000006U'
SELECT STUFF((SELECT '; '+Data
FROM
(
SELECT Split.a.value('.','nvarchar(1000)') AS Data
FROM
(
SELECT
CAST('<S>'+REPLACE(Data,';','</S><S>') +'</S>' AS XML ) AS Data
FROM #Table
)AS A
CROSS APPLY Data.nodes('S') AS Split(a)
)dt
WHERE CHARINDEX('U',Data)=0 FOR XML PATH('')),1,1,'') AS Data
Result
Data
---------
000002; 000004
As mentioned in the comments, SQL Server does not have any native regex replacement support. But, if you can get a dump of your entire table/column, then you can easily do a regex replacement in another tool, such as Notepad++.
Do a find on this pattern:
[0-9]+U;?
And then just replace with empty string. This should leave each row with the data you want to see. Here is a demo showing that this works in Java.
Demo
for SQL Server 2016 and later.
select stuff (
(select ',' + value
from STRING_SPLIT ('000001U;000002;000003U;000004;000005U;000006U', ';')
where right(value, 1) <> 'U'
for xml path('')),
1, 1, '')
for earlier version, you may use any CSV Spliter like this from Jeff Moden http://www.sqlservercentral.com/articles/Tally+Table/72993/
Simple way is to determine the value by IsNumeric function.
DECLARE #GIVEN VARCHAR(MAX)='000001U;000002;000003U;000004;000005U;000006U';
DECLARE #FINAL VARCHAR(MAX)='';
SELECT #FINAL =#FINAL+ case when ISNUMERIC(val)=1 then val+';' else '' end FROM (
SELECT split.x.value('.','varchar(max)') VAL FROM(
SELECT CAST('<M>'+REPLACE(#GIVEN,';','</M><M>')+'</M>' AS XML) AS VAL
)A
CROSS APPLY a.VAL.nodes('/M') as split(x)
)AA
PRINT #FINAL
Result: 000002;000004;
Question: how to split below string using XML?
Input:
'7-VPN Connectivity 7.8 - Ready to Elixir Connector install 9-Unified Installation'
Expected output:
7-VPN Connectivity
7.8 - Ready to Elixir Connector install
9-Unified Installation
My code:
DECLARE #xml AS XML,
#str AS VARCHAR(100)
SET #str = '7-VPN Connectivity 7.8 - Ready to Elixir Connector install 9-Unified Installation'
SET #xml = CAST(('<X>'+replace(#str,' ','</X><X>')+'</X>') AS XML)
SELECT
N.value('.', 'VARCHAR(10)') AS value
FROM
#xml.nodes('X') AS T(N)
--Provide the comma From Where you wan't to split The Data
-- For Eg:
BEGIN TRAN
DECLARE #S varchar(max),
#Split char(1),
#X xml
SELECT #S = '7-VPN Connectivity ,7.8- Ready to Elixir Connector install, 9-Unified Installation',
#Split = ','
SELECT #X = CONVERT(xml,' <root> <s>' + REPLACE(#S,#Split,'</s> <s>') + '</s> </root> ')
SELECT [Value] = T.c.value('.','varchar(255)')
FROM #X.nodes('/root/s') T(c)
ROLLBACK TRAN
This is a horrible design! If there is the slightest chance to fix this you should change this the sooner the better...
You might try something like this, but use it only to clean up that mess!
DECLARE #YourString VARCHAR(100)='7-VPN Connectivity 7.8 - Ready to Elixir Connector install 9-Unified Installation';
WITH CutAtHyphen(Nr,part) AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
,LTRIM(RTRIM(A.part.value('text()[1]','nvarchar(max)')))
FROM
(
SELECT CAST('<x>' + REPLACE((SELECT #YourString AS [*] FOR XML PATH('')),'-','</x><x>') + '</x>' AS XML) AS Casted
) AS t
CROSS APPLY t.Casted.nodes('/x') AS A(part)
)
,CutOffFinal AS
(
SELECT Nr
,part
,LEFT(part,LEN(part)-PositionOf.LastBlank) AS Remainder
,CASE WHEN Nr>1 THEN RIGHT(part,PositionOf.LastBlank) ELSE part END AS Tail
FROM CutAtHyphen
OUTER APPLY (SELECT CHARINDEX(' ',REVERSE(part))) AS PositionOf(LastBlank)
)
,recCTE AS
(
SELECT Nr, CAST(N'' AS NVARCHAR(MAX)) AS String,Tail FROM CutOffFinal WHERE Nr=1
UNION ALL
SELECT cof.Nr
,r.Tail + '-' + cof.Remainder
,cof.Tail
FROM recCTE AS r
INNER JOIN CutOffFinal AS cof ON cof.Nr=r.Nr+1
)
SELECT String + CASE WHEN Nr=(SELECT MAX(Nr) FROM CutOffFinal) THEN Tail ELSE '' END AS FinalString
FROM recCTE
WHERE Nr>1;
This code will first of all cut the string at the hyphens and trim it. The it will search for the last blank and cut of the number, which belongs to the next row.
The recursive CTE will travel down the line and concatenate the tail of the previous row, with the remainder of the current.
The first and the last line need special treatment.
I realize this question has been asked before, but I can't get it to work for some reason.
I'm using the split function from this SQL Team thread (second post) and the following queries.
--This query converts the interests field from text to varchar
select
cp.id
,cast(cp.interests as varchar(100)) as interests
into #client_profile_temp
from
client_profile cp
--This query is supposed to split the csv ("Golf","food") into multiple rows
select
cpt.id
,split.data
from
#client_profile_temp cpt
cross apply dbo.split(
cpt.interests, ',') as split <--Error is on this line
However I'm getting an
Incorrect syntax near '.'
error where I've marked above.
In the end, I want
ID INTERESTS
000CT00002UA "Golf","food"
to be
ID INTERESTS
000CT00002UA "Golf"
000CT00002UA "food"
I'm using SQL Server 2008 and basing my answer on this StackOverflow question. I'm fairly new to SQL so any other words of wisdom would be appreciated as well.
TABLE
x-----------------x--------------------x
| ID | INTERESTS |
x-----------------x--------------------x
| 000CT00002UA | Golf,food |
| 000CT12303CB | Cricket,Bat |
x------x----------x--------------------x
METHOD 1 : Using XML format
SELECT ID,Split.a.value('.', 'VARCHAR(100)') 'INTERESTS'
FROM
(
-- To change ',' to any other delimeter, just change ',' before '</M><M>' to your desired one
SELECT ID, CAST ('<M>' + REPLACE(INTERESTS, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM TEMP
) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
SQL FIDDLE
METHOD 2 : Using function dbo.Split
SELECT a.ID, b.items
FROM #TEMP a
CROSS APPLY dbo.Split(a.INTERESTS, ',') b
SQL FIDDLE
And dbo.Split function is here.
CREATE FUNCTION [dbo].[Split](#String varchar(8000), #Delimiter char(1))
returns #temptable TABLE (items varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
FINAL RESULT
from
#client_profile_temp cpt
cross apply dbo.split(
#client_profile_temp.interests, ',') as split <--Error is on this line
I think the explicit naming of #client_profile_temp after you gave it an alias is a problem, try making that last line:
cpt.interests, ',') as split <--Error is on this line
EDIT You say
I made this change and it didn't change anything
Try pasting the code below (into a new SSMS window)
create table #client_profile_temp
(id int,
interests varchar(500))
insert into #client_profile_temp
values
(5, 'Vodka,Potassium,Trigo'),
(6, 'Mazda,Boeing,Alcoa')
select
cpt.id
,split.data
from
#client_profile_temp cpt
cross apply dbo.split(cpt.interests, ',') as split
See if it works as you expect; I'm using sql server 2008 and that works for me to get the kind of results I think you want.
Any chance when you say "I made the change", you just changed a stored procedure but haven't run it, or changed a script that creates a stored procedure, and haven't run that, something along those lines? As I say, it seems to work for me.
As this is old, it seems the following works in SQL Azure (as of 3/2022)
The big changes being split.value instead of .data or .items as shown above; no as after the function, and lastly string_split is the method.
select Id, split.value
from #reportTmp03 rpt
cross apply string_split(SelectedProductIds, ',') split
Try this:
--This query is supposed to split the csv ("Golf","food") into multiple rows
select
cpt.id
,split.data
from
#client_profile_temp cpt
cross apply dbo.split(cpt.interests, ',') as split <--Error is on this line
You must use table alias instead of table name as soon as you define it.
I have a query as follows to display #prodid= ''1,2,10,4,5,6,7,8,13,16,17,3'' from the string 'dbo.proudction #prodid= ''1,2,10,4,5,6,7,8,13,16,17,3'' ,#stock= 0':
declare #T table(Col1 varchar(100))
insert into #T values ('dbo.proudction #prodid= ''1,2,10,4,5,6,7,8,13,16,17,3'' ,#stock= 0')
select
substring(Col1, Start, Stop-Start)
from #T
cross apply
(select charindex('''', Col1)+1) c1(Start)
cross apply
(select charindex('''', Col1, Start)) c2(Stop)
Can you help me to understand the working of this query, please ?
The first cross apply use charindex locate the first quote in the string. The second cross apply locates the second quote in the string. Then substring will extract the text between the first quote and the second quote.
Result
1,2,10,4,5,6,7,8,13,16,17,3