Find position of delimited character in a String (SQL Server) - sql

I have a string Variable
test=HARIA|123|MALE|STUDENT|HOUSEWIFE
i am using | as delimited character. if i want to extract data from 2nd occurence of pipe till 3rd occurrence. i need to get 'MALE' from above string.
any help appreciated

Untested
SELECT
SUBSTRING (
#test,
CHARINDEX('|', #test, CHARINDEX('|', #test) + 1) + 1,
CHARINDEX('|', #test, CHARINDEX('|', #test, CHARINDEX('|', #test) + 1) + 1) - 1
)
A nicer way would be split the string into a table, and use ROW_NUMBER() to extract the 3rd element. Based on this Arrays and Lists in SQL Server
DECLARE #test varchar(100) = 'HARIA|123|MALE|STUDENT|HOUSEWIFE'
SELECT TOP 8000
Num
INTO
#Number
FROM
(
SELECT
ROW_NUMBER() OVER (ORDER BY c1.object_id) AS Num
FROM
sys.columns c1, sys.columns c2, sys.columns c3
) N
SELECT
ROW_NUMBER() OVER (ORDER BY Num) AS Rank,
LTRIM(RTRIM(SUBSTRING(#test,
Num,
CHARINDEX('|', #test + '|', Num) - Num
))) AS Value
FROM
#Number
WHERE
Num <= LEN (#test)
AND
SUBSTRING('|' + #test, Num, 1) = '|'
DROP TABLE #Number

Try this
Solution 1:(Using a number table)
declare #str varchar(1000)
set #str ='HARIA|123|MALE|STUDENT|HOUSEWIFE'
--Creating a number table
;with numcte as(
select 1 as rn union all select rn+1 from numcte where rn<LEN(#str)),
--Get the position of the "|" charecters
GetDelimitedCharPos as(
select ROW_NUMBER() over(order by getdate()) seq, rn,delimitedcharpos
from numcte
cross apply(select SUBSTRING(#str,rn,1)delimitedcharpos) X where delimitedcharpos = '|')
--Applying the formula SUBSTRING(#str,startseq + 1,endseq-startseq + 1)
-- i.e. SUBSTRING(#str,11,15-11) in this case
select top 1 SUBSTRING(
#str
,(select top 1 rn+1 from GetDelimitedCharPos where seq =2)
,(select top 1 rn from GetDelimitedCharPos where seq =3) -
(select top 1 rn+1 from GetDelimitedCharPos where seq =2)
) DesiredResult
from GetDelimitedCharPos
Solution 2:(Using XQuery)
DECLARE #xml as xml,#str as varchar(100),#delimiter as varchar(10)
SET #str='HARIA|123|MALE|STUDENT|HOUSEWIFE'
SET #xml = cast(('<X>'+replace(#str,'|' ,'</X><X>')+'</X>') as xml)
SELECT ShrededData as DesiredResult FROM(
SELECT
ROW_NUMBER() over(order by getdate()) rn
,N.value('.', 'varchar(10)') as ShrededData FROM #xml.nodes('X') as T(N))X
WHERE X.rn = 3 -- Specifying the destination sequence value(here 3)
Output(in both the cases)
DesiredResult
MALE

I found this. Using a t-Sql for loop. Good reference of syntax, too.

Related

How to replace anything between 2 specific characters in SQL Server

I'm trying to replace anything between 2 specific characters in a string that contains multiples of those 2 caracters. Take it as a csv format.
Here an example of what i got as data in that field:
0001, ABCD1234;0002, EFGH432562;0003, IJKL1345hsth;...
What I need to retreive from it is all parts before the ',' but not what are between ',' and ';'
I tried with those formula but no success
SELECT REPLACE(fieldname, ',[A-Z];', ' ') FROM ...
or
SELECT REPLACE(fieldname, ',*;', ' ') FROM ...
I need to get
0001 0002 0003
Is there a way to achieve that?
You can CROSS APPLY to a STRING_SPLIT that uses STRING_AGG (since Sql Server 2017) to stick the numbers back together.
select id, codes
from your_table
cross apply (
select string_agg(left(value, patindex('%_,%', value)), ' ') as codes
from string_split(fieldname, ';') s
where value like '%_,%'
) ca;
GO
id
codes
1
0001 0002 0003
Demo on db<>fiddle here
Extra
Here is a version that also works in Sql Server 2014.
Inspired by the research from #AaronBertrand
The UDF uses a recursive CTE to split the string.
And the FOR XML trick is used to stick the numbers back together.
CREATE FUNCTION dbo.fnString_Split
(
#str nvarchar(4000),
#delim nchar(1)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
WITH RCTE AS (
SELECT
1 AS ordinal
, ISNULL(NULLIF(CHARINDEX(#delim, #str),0), LEN(#str)) AS pos
, LEFT(#str, ISNULL(NULLIF(CHARINDEX(#delim, #str),0)-1, LEN(#str))) AS value
UNION ALL
SELECT
ordinal+1
, ISNULL(NULLIF(CHARINDEX(#delim, #str, pos+1), 0), LEN(#str))
, SUBSTRING(#str, pos+1, ISNULL(NULLIF(CHARINDEX(#delim, #str, pos+1),0)-pos-1, LEN(#str)-pos ))
FROM RCTE
WHERE pos < LEN(#str)
)
SELECT ordinal, value
FROM RCTE
);
SELECT id, codes
FROM your_table
CROSS APPLY (
SELECT RTRIM((
SELECT LEFT(value, PATINDEX('%_,%', value))+' '
FROM dbo.fnString_Split(fieldname, ';') AS spl
WHERE value LIKE '%_,%'
ORDER BY ordinal
FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)')
) AS codes
) ca
OPTION (MAXRECURSION 250);
id
codes
1
0001 0002 0003
Demo on db<>fiddle here
Alternative version of the UDF (no recursion)
CREATE FUNCTION dbo.fnString_Split
(
#str NVARCHAR(4000),
#delim NCHAR(1)
)
RETURNS #tbl TABLE (ordinal INT, value NVARCHAR(4000))
WITH SCHEMABINDING
AS
BEGIN
DECLARE #value NVARCHAR(4000)
, #pos INT = 0
, #ordinal INT = 0;
WHILE (LEN(#str) > 0)
BEGIN
SET #ordinal += 1;
SET #pos = ISNULL(NULLIF(CHARINDEX(#delim, #str),0), LEN(#str)+1);
SET #value = LEFT(#str, #pos-1);
SET #str = SUBSTRING(#str, #pos+1, LEN(#str));
INSERT INTO #tbl (ordinal, value)
VALUES (#ordinal, #value);
END;
RETURN;
END;
If you're on SQL Server 2017 and don't need a guarantee that the order will be maintained, then LukStorms' answer is perfectly adequate.
However, if you:
care about an order guarantee; or,
are on an older version than 2017 (and can't use STRING_AGG); or,
are on an even older version than 2016 or are in an older compatibility level (and can't use STRING_SPLIT):
Here's an ordered split function that can help (it's long and ugly but you only have to create it once):
CREATE FUNCTION dbo.SplitOrdered
(
#list nvarchar(max),
#delim nvarchar(10)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
WITH w(n) AS (SELECT 0 FROM (VALUES (0),(0),(0),(0)) w(n)),
k(n) AS (SELECT 0 FROM w a, w b),
r(n) AS (SELECT 0 FROM k a, k b, k c, k d, k e, k f, k g, k h),
p(n) AS (SELECT TOP (COALESCE(LEN(#list), 0))
ROW_NUMBER() OVER (ORDER BY ##SPID) -1 FROM r),
spots(p) AS
(
SELECT n FROM p
WHERE (SUBSTRING(#list, n, LEN(#delim + 'x') - 1) LIKE #delim OR n = 0)
),
parts(p,val) AS
(
SELECT p, SUBSTRING(#list, p + LEN(#delim + 'x') - 1,
LEAD(p, 1, 2147483647) OVER (ORDER BY p) - p - LEN(#delim))
FROM spots AS s
)
SELECT listpos = ROW_NUMBER() OVER (ORDER BY p),
Item = LTRIM(RTRIM(val))
FROM parts
);
Then the query can become:
;WITH x AS
(
SELECT id, listpos,
codes = LEFT(Item, COALESCE(NULLIF(CHARINDEX(',', Item),0),1)-1)
FROM dbo.your_table
CROSS APPLY dbo.SplitOrdered(fieldname, ';') AS c
)
SELECT id, codes = (
(SELECT x2.codes + ' '
FROM x AS x2
WHERE x2.id = x.id
ORDER BY x2.listpos
FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)')
)
FROM x GROUP BY id;
Example borrowing from LukStorms' db<>fiddle
Note that, in addition to guaranteeing order and being backward compatible (well, only back so many versions), it also ignores garbage data, e.g. try:
0001, ABCD1234;0002 but no comma

Reverse order of elements in a string

I have the following string:
1119/2/483/11021
I would like to reverse the order of the elements in that string. Desired output:
11021/483/2/1119
T-SQL Version 2014
You need an ordered split function, e.g. (inspiration):
CREATE FUNCTION dbo.SplitOrdered
(
#list nvarchar(max),
#delim nvarchar(10)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
WITH w(n) AS (SELECT 0 FROM (VALUES (0),(0),(0),(0)) w(n)),
k(n) AS (SELECT 0 FROM w a, w b),
r(n) AS (SELECT 0 FROM k a, k b, k c, k d, k e, k f, k g, k h),
p(n) AS (SELECT TOP (COALESCE(LEN(#list), 0))
ROW_NUMBER() OVER (ORDER BY ##SPID) -1 FROM r),
spots(p) AS
(
SELECT n FROM p
WHERE (SUBSTRING(#list, n, LEN(#delim + 'x') - 1) LIKE #delim OR n = 0)
),
parts(p,val) AS
(
SELECT p, SUBSTRING(#list, p + LEN(#delim + 'x') - 1,
LEAD(p, 1, 2147483647) OVER (ORDER BY p) - p - LEN(#delim))
FROM spots AS s
)
SELECT listpos = ROW_NUMBER() OVER (ORDER BY p),
Item = LTRIM(RTRIM(val))
FROM parts
);
Then you can reassemble using STRING_AGG() (if SQL Server 2017 or better) or FOR XML PATH on lower versions:
SQL Server 2017 +
DECLARE #OriginalString nvarchar(255) = N'1119/2/483/11021';
SELECT NewString = STRING_AGG(o.Item, N'/')
WITHIN GROUP (ORDER BY listpos DESC)
FROM dbo.SplitOrdered(#OriginalString, N'/') AS o;
SQL Server < 2017
DECLARE #OriginalString nvarchar(255) = N'1119/2/483/11021';
SELECT NewString = STUFF(
(SELECT N'/' + o.Item
FROM dbo.SplitOrdered(#OriginalString, N'/') AS o
ORDER BY o.listpos DESC
FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)'),1,1,N'');
Example db<>fiddle
Please try the following solution based on the built-in PARSENAME() T-SQL function.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, Tokens VARCHAR(MAX));
INSERT INTO #tbl (Tokens) VALUES
('1119/2/483/11021'),
('1120/25/484/1102');
-- DDL and sample data population, end
SELECT tbl.*
, PARSENAME(c, 1) + '/' +
PARSENAME(c, 2) + '/' +
PARSENAME(c, 3) + '/' +
PARSENAME(c, 4) AS Result
FROM #tbl AS tbl
CROSS APPLY (VALUES (REPLACE(Tokens, '/', '.') )) AS t(c);
Output
+----+------------------+------------------+
| ID | Tokens | Result |
+----+------------------+------------------+
| 1 | 1119/2/483/11021 | 11021/483/2/1119 |
| 2 | 1120/25/484/1102 | 1102/484/25/1120 |
+----+------------------+------------------+
First, split the string and convert it into a column then order by desc and display into multiple row values into a single row. In the following code, you can set any string and split char.
Try following way.
DECLARE #S varchar(max) ,
#Split char(1),
#X xml
DECLARE #Names VARCHAR(8000)
SELECT #S = '1119/2/483/11021',
#Split = '/'
SELECT #X = CONVERT(xml,' <root> <myvalue>' +
REPLACE(#S,#Split,'</myvalue> <myvalue>') + '</myvalue> </root> ')
select #Names = COALESCE(#Names + '/', '') + Value from (
select rowno,Value from (
select ROW_NUMBER() OVER(ORDER BY d) AS rowno , Value from (
SELECT T.c.value('.','varchar(20)') as Value,0 as d
FROM #X.nodes('/root/myvalue') T(c)
) m
) r
) t order by t.rowno desc
select #Names as ReverseString
Splitting the string into sub-strings, and then joining them back up, is most likely going to be a good approach.
Some comments mention using string-reverse, but that doesnt seem to be a good approach at all in your case, since you just want to reverse the order of words within the current string, not actually reverse the entire text-string character-by-character.
PS: string_split does not guarantee the order of the chunks!

Add character in front and at the end of each character

In SQL I want to add 0 in front and , at the end of each character.
Example: A30F1 -> 0A,03,00,0F,01
I don't want to use cursor if possible.
Thanks!
EIDT:
I apologize for not asking the most appropriate question at the beginning.
In short, I have a table and for each value in the column name I have to convert it to the desired format. For example, we have a #Temp table:
CREATE TABLE #Temp (id INT, name VARCHAR(25))
INSERT INTO #Temp VALUES (1, 'A30F1'), (2, 'B51R9'), (3, 'L1721')
SELECT * FROM #Temp
One method would be to use a Tally to split the string into it's individual characters, and then use concatenation to add the 0 to the start, and STRING_AGG to comma delimit the results:
DECLARE #YourValue varchar(5) = 'A30F1';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (LEN(#YourValue))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2) --Up to 100 characters, add more cross joins for more characters
SELECT STRING_AGG(CONCAT('0',SS.C),',') WITHIN GROUP (ORDER BY T.I) AS NewString
FROM (VALUES(#YourValue))V(YourValue)
CROSS JOIN Tally T
CROSS APPLY (VALUES(SUBSTRING(V.YourValue,T.I,1)))SS(C);
It appears this is meant to be against a table, not a single value. This needs, however, very few changes to work against a table:
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (SELECT MAX(LEN(YourColumn)) FROM dbo.YourTable)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2) --Up to 100 characters, add more cross joins for more characters
SELECT STRING_AGG(CONCAT('0',SS.C),',') WITHIN GROUP (ORDER BY T.I) AS NewString
FROM dbo.YourTable YT
JOIN Tally T ON LEN(YT.YourColumn) >= T.I
CROSS APPLY (VALUES(SUBSTRING(YT.YourColumn,T.I,1)))SS(C)
GROUP BY YT.YourColumn;
db<>fiddle
I solved the simplest possible with a few variables, WHILE and SUBSTRING
DECLARE #var VARCHAR(20) = 'A30F1', #i INT = 1, #res NVARCHAR(20)
WHILE (#i <= LEN(#var))
BEGIN
SET #res = #res + '0' + SUBSTRING(#var, #i, 1) + ','
SET #i = #i + 1
END
SELECT LEFT(#res, LEN(#res) - 1) output
Check demo on DB<>FIDDLE.
Original answer:
A recursive CTE and a STRING_AGG() call is also an option (SQL Server 2017+ is needed):
DECLARE #text varchar(max) = 'A30F1';
WITH rCTE AS
(
SELECT 1 AS CharacterPosition, SUBSTRING(#text, 1, 1) AS Character
UNION ALL
SELECT CharacterPosition + 1, SUBSTRING(#text, CharacterPosition + 1, 1)
FROM rCTE
WHERE CharacterPosition < LEN(#text)
)
SELECT STRING_AGG('0' + Character, ',') WITHIN GROUP (ORDER BY CharacterPosition)
FROM rCTE
OPTION (MAXRECURSION 0);
Update:
You need a different statement, if the names are stored in a table, again using recursion and STRING_AGG():
Table:
CREATE TABLE #Temp (id INT, name VARCHAR(25))
INSERT INTO #Temp VALUES (1, 'A30F1'), (2, 'B51R9'), (3, 'L1721')
Statement:
; WITH rCTE AS (
SELECT
t.id AS id,
LEFT(t.name, 1) AS Character,
STUFF(t.name, 1, 1, '') AS CharactersRemaining,
1 AS CharacterPosition
FROM #Temp t
UNION ALL
SELECT
r.id,
LEFT(r.CharactersRemaining, 1),
STUFF(r.CharactersRemaining, 1, 1, ''),
CharacterPosition + 1
FROM rCTE r
WHERE LEN(r.CharactersRemaining) > 0
)
SELECT
id,
STRING_AGG('0' + Character, ',') WITHIN GROUP (ORDER BY CharacterPosition) AS name
FROM rCTE
GROUP BY id
OPTION (MAXRECURSION 0);
Result:
id name
1 0A,03,00,0F,01
2 0B,05,01,0R,09
3 0L,01,07,02,01
If you are only applying this to English alphabet characters and digits as in your example you could do this.
CREATE TABLE #Temp (id INT, name VARCHAR(25))
INSERT INTO #Temp VALUES (1, 'A30F1'), (2, 'B51R9'), (3, 'L1721'), (4, 'A')
SELECT SUBSTRING(REPLACE(
0x00 + CAST(CAST(name AS NVARCHAR(25)) AS BINARY(50)), CHAR(0), '0,')
, 3
, LEN(name) * 3 - 1)
FROM #Temp
returns
0A,03,00,0F,01
0B,05,01,0R,09
0L,01,07,02,01
0A
This takes advantage of the fact that the binary representation of the nvarchar and varchar is the same for this limited character set except for padding out with 0x00
'A30F1' -> 0x4133304631
N'A30F1' -> 0x41003300300046003100

extract all decimal value from a string in sql server

I want to extract all decimal values from a string:
'1.1aa' = 1.1,
'-1.1al3' = -1.13,
' -' = 0
'21d.2 d .3-d' = 21.23
What will be the query to extract this output?
DECLARE #Col VARCHAR(100)='-c 43.f4gh57t';
While (PATINDEX('%[^0-9.-]%', #Col) > 0)
begin
SET #Col = STUFF(#Col, PATINDEX('%[^-0-9.]%', #Col), 1, '');
end
SELECT CAST(#Col AS DECIMAL(18, 2));
I have written this query but it is not working with these values:
' -',
'21d.2 d .3-d'
DECLARE #Col VARCHAR(100)= '--A12345-..6789-.';
While (PATINDEX('%[^0-9.-]%', #Col) > 0)
begin
SET #Col = STUFF(#Col, PATINDEX('%[^-0-9.]%', #Col), 1, '');
end
--remove all hyphens but keep the first one (if first char with numbers following)
select #Col = case when #Col like '-%[0-9]%' then '-' else '' end + replace(#Col, '-', '')
where #Col like '%-%';
--remove all dots but keep the first one (if followed by numbers)
select #Col = stuff(replace(#Col, '.', '')+' ', charindex('.', #Col), 0, case when #Col like '%.%[0-9]%' then '.' else '' end)
where #Col like '%.%';
select #col as _col;
SELECT CAST(case when #col = '' then '0' else #col end AS DECIMAL(20,10)) as _decimal;
You could do this with Tally, though if you have multiple '.' characters or a '-' in the middle of the string it will fail on conversion:
DECLARE #Value varchar(100) = '-c 43.f4gh57t';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (LEN(#Value))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2),
Chars AS(
SELECT T.I,
SUBSTRING(#Value,T.I,1) AS C
FROM Tally T)
SELECT TRY_CONVERT(decimal(25,10),STRING_AGG(C.C,'') WITHIN GROUP (ORDER BY C.I)) AS Number
FROM Chars C
WHERE C.C LIKE '[0-9.-]';
If you're using an older version of SQL Server, you'll need to use the "old" FOR XML PATH (and STUFF) technique instead of STRING_AGG.
Version 2: This should eliminate the errors I describe above:
DECLARE #Value varchar(100) = '-c 4-3.f4gh.57t';
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (LEN(#Value))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2),
Chars AS(
SELECT T.I,
SUBSTRING(#Value,T.I,1) AS C
FROM Tally T),
RNs AS(
SELECT C.C,
ROW_NUMBER() OVER (ORDER BY C.I) AS RN,
ROW_NUMBER() OVER (PARTITION BY C.C ORDER BY C.I) AS cRN
FROM Chars C
WHERE C.C LIKE '[0-9.-]')
SELECT TRY_CONVERT(decimal(25,10),STRING_AGG(V.C,'') WITHIN GROUP (ORDER BY R.RN)) AS Number
FROM RNs R
CROSS APPLY (VALUES(CASE R.C WHEN '-' THEN CASE R.RN WHEN 1 THEN R.C END
WHEN '.' THEN CASE R.cRN WHEN 1 THEN R.C END
ELSE R.C
END))V(C);

How to get first character beside underscore for a given string

input:
string
'abc_def_ghk_lmn'
output:
dgl
You can try this (or even create a function):
DECLARE #str varchar(250) = 'abc_def_ghk_lmn'
DECLARE #result varchar(250)='';
WHILE(charindex('_',#str)!=0)
BEGIN
DECLARE #position int = charindex('_',#str)
SET #result += substring(#str,#position+1,1)
SET #str = substring(#str,#position+1,len(#str))
END
SELECT #result
You can use a recursive CTE:
IF OBJECT_ID('tempdb..#t') is not null drop table #t
SELECT * into #t from (values (N'abc_def_ghk_lmn'), (N'a_f_k_n'), (null), ('____'), ('asasas'), ('a_sasas'), ('asas_')) T(val);
;WITH CTE AS
(
SELECT
Cast(SUBSTRING(T.val, CHARINDEX('_',T.val,1) + 1, 1) as nvarchar(4000)) FC
,CHARINDEX('_', T.val, 1) CI
,val
,0 [level]
from #t T
where CHARINDEX('_', T.val, 1) > 0
union all
SELECT
Cast(T.FC + SUBSTRING(T.val, CHARINDEX('_',T.val,T.CI+1) + 1, 1) as nvarchar(4000)) FC
,CHARINDEX('_', T.val, T.CI+1) CI
,val
,t.[level] + 1
from CTE T
where CHARINDEX('_',T.val,T.CI+1) > 0
)
, Res AS
(
SELECT
*
,ROW_NUMBER() OVER (Partition by val order by [level] desc) RN
from CTE
)
SELECT * from Res where RN = 1
This uses Jeff Moden's DelimitedSplit8K Function. Firstly because i don't know what version of SQL Server you are using, and secondly, the inbuilt function STRING_SPLIT (available in SQL Server 2016 onwards) doesn't include an Item Number value (thus how does one exclude the first result?):
SELECT (SELECT LEFT(Item, 1)
FROM DelimitedSplit8K ('abc_def_ghk_lmn','_') DS
WHERE DS.ItemNumber > 1
FOR XML PATH(''));
Edit:
Example with a dataset:
WITH VTE AS(
SELECT *
FROM (VALUES ('asdgsad_sdfh_sadfh'),('_ashdf+ashd'),('jsda_sdkhfsdjf_654_asdfkhasd_567465413_kasbgdjkasdj')) V(S))
SELECT (SELECT LEFT(Item, 1)
FROM DelimitedSplit8K (S,'_') DS
WHERE DS.ItemNumber > 1
FOR XML PATH('')) AS FirstCharacters
FROM VTE;
Try this:
DECLARE #String VARCHAR(50)= 'abc_def_ghk_lmn',#Result VARCHAR(10)=''
WHILE CHARINDEX('_',#String)>0
BEGIN
SELECT #Result=#Result + SUBSTRING(#String,CHARINDEX('_',#String)+1,1)
SELECT #String=RIGHT(#String,LEN(#String)- CHARINDEX('_',#String))
END
SELECT #Result FinalResult
OUTPUT:
FinalResult
dgl
Please try this -...Always use SET BASED Approach
SOLUTION
DECLARE #x AS XML=''
DECLARE # AS VARCHAR(1000) = 'abc_def_ghk_lmn_'
SET #x = CAST('<A>'+ REPLACE(#,'_','</A><A>')+ '</A>' AS XML)
;WITH CTE AS
(
SELECT t.value('.', 'VARCHAR(10)') Value FROM #x.nodes('/A') AS x(t)
)
,CTE1 AS
(
SELECT * , ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) rnk FROM CTE
)
,CTE2 AS
(
SELECT SUBSTRING(Value,1,1) v , rnk FROM CTE1
)
SELECT CASE WHEN LEFT(#,1) <> '_' THEN MAX(SUBSTRING(u,2,LEN(u))) ELSE MAX(u) END finalstr from (
SELECT
(
SELECT '' + v
FROM CTE2 a
FOR XML PATH('')
)u
FROM CTE2 )x
OUTPUT
finalstr
---------------
dgl
(1 row affected)
Try this below
DECLARE #str TAble(String varchar(250))
INSERT INTO #str
SELECT 'abc_def_ghk_lmn_opq_rst_uvw_xyz'
SELECT STUFF((SELECT ''+LEFT(String,1) FROM
(
SELECT Split.a.value('.','Varchar(1000)') As String,
ROW_NUMBER()OVER(ORDER BY (SELECT 1)) AS Id FROM
(
SELECT CASt('<S>'+REPLACE(String,'_','</S><S>')+'</S>' AS XML )AS String
FROM #str
)as A
CROSS APPLY String.nodes ('S') AS Split(a)
)dt
WHERE dt.Id <>1
FOR XML PATH ('')),1,0,'') AS ExpectedColumn
Result
ExpectedColumn
--------------
dglorux
Use LEFT to get the left part and INSTR to find underscore and finally get your string:
SELECT LEFT(FIELD_1, CHARINDEX('_', FIELD_1) - 2) AS [ANY_ALIAS]
FROM TABLE_1;
EDIT: Now is MSSQL... ;D