How to sub-string in SQL with 4 same consecutive characters - sql

I want to sub-string 11.1.2.3.4.5 or 10.1.2.4.5 and so on to be split until 4(dot) only like 11.1.2.3 and 10.1.2.3 likewise.
Can someone help to achieve this in SQL?

You could use a recurcive CTE as the following
CREATE TABLE Strings( S VARCHAR(25) );
INSERT Strings VALUES
('1.2.3.4.5.6'),
('11.2.12.5.66'),
('y.888.p.666.2.00');
WITH CTE AS
(
SELECT 1 N, CHARINDEX('.', S) Pos, S
FROM Strings
UNION ALL
SELECT N + 1, CHARINDEX('.', S, Pos + 1), S
FROM CTE
WHERE Pos > 0
)
SELECT S, SUBSTRING(S, 1, Pos - 1) --or use LEFT()
FROM CTE
WHERE N = 4;
Or using a nested CHARINDEX() as
SELECT S, LEFT(S, CI-1)
FROM Strings
CROSS APPLY
(
VALUES
(CHARINDEX('.', S, CHARINDEX('.', S, CHARINDEX('.', S, CHARINDEX('.', S)+1)+1)+1))
) T(CI)
Here is a db<>fiddle

Related

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

Get the Capital letters from a string

I have this requirement to extract the capital letters from a column in SQL Server.
EX: ABC_DEF_ghi
I only want to extract ABC_DEF.
Sometimes the string could be like ABC_DEF_GHI_jkl, so in this case it will be ABC_DEF_GHI
Any suggestions would be helpful.
Thanks in advance.
As Tim Biegeleisen mentioned, this isn't easy in SQL Server, as it doesn't support regular expressions. As such you have to be some what inventive.
As we don't know what version of SQL Server you are using (though I did ask) I am assuming you are using the latest version of SQL Server, and have access to both STRING_AGG and TRIM. If not, you'll need to use the old FOR XML PATH and STUFF method for string aggregation, and LTRIM and RTRIM with nested REPLACEs for TRIM.
Anyway, what I do here is collate the value to a binary collation that is both case sensitive and also orders the letters in Uppercase and then Lowercase (though a collation that does Lowercase and then Uppercase would be fine too, it's just important it's not alphabetically and then case). So in an order like ABC...Zabc...z rather than like AaBb...Zz. I then use a Tally to split the collated string into it's individual characters.
I then use STRING_AGG with a CASE expression to only retain the Underscore characters (which you appear to want as well) and just the uppercase letters. Finally I use TRIM to remove any leading and trailing underscores; without this the value returned would be 'ABC_DEF_GHI_'.
I also assume you are doing this against a table, rather than a scalar value, which gives this:
DECLARE #SomeString varchar(100) = 'ABC_DEF_GHI_jkl';
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(V.SomeString)) FROM (VALUES(#SomeString))V(SomeString)) --This would be your table
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2) --100 rows, add more cross joins for more rows
SELECT TRIM('_' FROM STRING_AGG(CASE WHEN SS.C LIKE '[A-Z_]' THEN SS.C END,'') WITHIN GROUP (ORDER BY T.I)) AS NewString
FROM (VALUES(#SomeString))V(SomeString) --This would be your table
CROSS APPLY (VALUES(V.SomeString COLLATE Latin1_General_BIN))C(SomeString) --Collate to a collation that is both case sensitive and orders Uppercase first
JOIN Tally T ON LEN(C.SomeString) >= T.I
CROSS APPLY (VALUES(SUBSTRING(C.SomeString,T.I,1)))SS(C) --Get each character
GROUP BY V.SomeString;
db<>fiddle
Of course, a "simpler" solution might be to find and implement a Regex CLR function and just use that. πŸ™ƒ
Turns out the OP is using 2014... This means the above needs some significant refactorying. I am afraid I don't explain how FOR XML PATH or REPLACE work here (as I put the effort into the original solution), however, a search will yield you the details.:
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(V.SomeString)) FROM (VALUES(#SomeString))V(SomeString)) --This would be your table
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2) --100 rows, add more cross joins for more rows
SELECT REPLACE(LTRIM(RTRIM(REPLACE((SELECT CASE WHEN SS.C LIKE '[A-Z_]' THEN SS.C END
FROM (VALUES(V.SomeString COLLATE Latin1_General_BIN))C(SomeString) --Collate to a collation that is both case sensitive and orders Uppercase first
JOIN Tally T ON LEN(C.SomeString) >= T.I
CROSS APPLY (VALUES(SUBSTRING(C.SomeString,T.I,1)))SS(C) --Get each character
ORDER BY T.I
FOR XML PATH(''),TYPE).value('(./text())[1]','varchar(100)'),'_',' '))),' ','_') AS NewString
FROM (VALUES(#SomeString))V(SomeString) --This would be your table
GROUP BY V.SomeString;
For SQL 2017 and upper :
DECLARE #SomeString varchar(100) = 'ABC_DEF_GHI_jkl';
WITH T0 AS
(
SELECT 1 AS INDICE,
SUBSTRING(#SomeString, 1, 1) AS RAW_LETTER,
SUBSTRING(UPPER(#SomeString), 1, 1) AS UP_LETTER
UNION ALL
SELECT INDICE + 1,
SUBSTRING(#SomeString, INDICE + 1, 1) AS RAW_LETTER,
SUBSTRING(UPPER(#SomeString), INDICE + 1, 1)
FROM T0
WHERE INDICE < LEN(#SomeString)
)
SELECT STRING_AGG(RAW_LETTER, '') WITHIN GROUP (ORDER BY INDICE)
FROM T0
WHERE RAW_LETTER COLLATE Latin1_General_BIN = UP_LETTER;
For SQL Server previous than 2017 :
WITH T0 AS
(
SELECT 1 AS INDICE,
SUBSTRING(#SomeString, 1, 1) AS RAW_LETTER,
SUBSTRING(UPPER(#SomeString), 1, 1) AS UP_LETTER
UNION ALL
SELECT INDICE + 1,
SUBSTRING(#SomeString, INDICE + 1, 1) AS RAW_LETTER,
SUBSTRING(UPPER(#SomeString), INDICE + 1, 1)
FROM T0
WHERE INDICE < LEN(#SomeString)
)
SELECT STUFF((SELECT '' + RAW_LETTER
FROM T0
WHERE RAW_LETTER COLLATE Latin1_General_BIN = UP_LETTER
ORDER BY INDICE
FOR XML PATH('')), 1, 0, '');

sql string split for defined number of char

i've a string like 'aabbcczx' and i need to split that string by 2 char.
The result expected is something like:
aabbcczx aa
aabbcczx bb
aabbcczx cc
aabbcczx zx
How can I do this?
consider also that the length of the string change row by row.
Thanks
If it's always 2 chars:
SELECT A.Val,
CA1.N,
SUBSTRING(A.Val,n,2)
FROM (
VALUES ('aabbcczx')
) AS A(Val)
CROSS
APPLY dbo.GetNums(1,LEN(A.Val)) AS CA1
WHERE CA1.n % 2 = 1;
GetNums is a number table/tally table generator you can find some several sources online.
It will provide the position of each character and we can use that in the substring start position. The where clause uses MOD to so we only show every other starting position
You can use a recursive query:
with cte as (
select convert(varchar(max), left(str, 2)) as val2, convert(varchar(max), stuff(str, 1, 2, '')) as rest, str
from (values ( 'aabbcczx' )) v(str)
union all
select left(rest, 2) as val2, stuff(rest, 1, 2, '') as rest, str
from cte
where rest <> ''
)
select str, val2
from cte;
You can use a recursive query to extract pairs of characters:
with instring as
( select 'aabbcczx' as s )
, splitter as
(
select s, substring(s, 1, 2) as rslt, 3 as next -- first two chars
from instring
union all
select s, substring(s, next, 2), next + 2 -- next two chars
from splitter
where len(s) >= next
)
select *
from splitter
See dbfiddle

extract text from the string

I need to extract text from the string KWR/50X50X5/1.4301 between /, or 50x50x5 in T-SQL. I've tried using Substing, however, does not go to me.
Ultimately, I need to add the values (sum values) ​​in between / without character x (for example, 50 + 50 + 5 = 105) I would be grateful for your help.
Try this:
DECLARE #t TABLE (id INT, v VARCHAR(100) )
INSERT INTO #t
VALUES ( 1, 'PWPQ/80X20/1.4301' ) ,
( 2, 'PWO/120/1.4404' ),
( 3, 'PWOI/120X9X90X80/1.4404' )
;WITH cte1 AS(SELECT id, SUBSTRING(v,
CHARINDEX('/', v) + 1,
CHARINDEX('/', v, CHARINDEX('/', v) + 1) - CHARINDEX('/', v) - 1) AS v
FROM #t),
cte2 AS(SELECT id, CAST ('<X>' + REPLACE(v, 'X', '</X><X>') + '</X>' AS XML) AS v FROM cte1)
SELECT id, SUM(Split.a.value('.', 'int')) AS v
FROM cte2 a CROSS APPLY v.nodes ('/X') AS Split(a)
GROUP BY id
Output:
id v
1 100
2 120
3 299
First cte is for extracting value between /.
Second cte for casting those values to xml format.
The last statement is standard trick for transposing string with delimeter to separate rows.
select substring(firstpart,1,CHARINDEX('/',firstpart)-1)
from
(select
substring(pattern,
CHARINDEX('/',pattern)+1,
datalength(pattern)) as firstpart
from tessst
)X;

In SQL Server , I want to separate string from '~'

I have problem. There are A~B~C~D values in my table.
I want separate this string like 'A' and 'B~C'.
And I could separate 'A' using this function
SELECT SUBSTRING(Item.Description, 0, CHARINDEX('~', Item.Description)) As Com
But after that, I can't separate 'B~C'.
Of course if I use SUBSTRING and CHARINDEX a lot, I can separate.
But it is very complicated.
So I wonder if I can use other ways.
Thx very much for reading
Here is a short SQL function that you can create in order to split your string:
CREATE FUNCTION dbo.Split (#sep char(1), #s varchar(8000))
RETURNS table
AS
RETURN (
WITH splitter_cte AS (
SELECT CHARINDEX(#sep, #s) as pos, 0 as lastPos
UNION ALL
SELECT CHARINDEX(#sep, #s, pos + 1), pos
FROM splitter_cte
WHERE pos > 0
)
SELECT SUBSTRING(#s, lastPos + 1,
case when pos = 0 then 80000
else pos - lastPos -1 end) as chunk
FROM splitter_cte
)
GO
And here is how you would use it:
SELECT * FROM dbo.Split('~', 'A~B~C~D')
OUTPUT:
chunk
-------------
A
B
C
D
Read more on how this sql split function works