This question already has answers here:
T-SQL Split Word into characters
(7 answers)
Closed 4 years ago.
I have String like as follows
Declare #string ='welcome'
and i want output like this
w
e
l
c
o
m
e
You can use a tally table for this, usually faster than looping, but you would have to test for yourself:
declare #s varchar(200)
set #s = 'Welcome'
;with t as (
select v
from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) t(v)
),
n as (
select row_number() over(order by (select null)) rn
from t t1 cross join t t2
)
select substring(#s, rn, 1)
from n
where rn <= len(#s)
DECLARE #string VARCHAR(256) = 'welcome'
DECLARE #cnt INT = 0;
WHILE #cnt < len(#string)
BEGIN
SET #cnt = #cnt + 1;
PRINT SUBSTRING ( #string ,#cnt , 1 )
END;
In essence you loop through the length of the string.
You print the character at the location of the index of the loop and print that.
You ca use recursive CTE.
Declare #string varchar(10) ='welcome'
;with cte as
(
select 1 as i,substring(#string,1,1) as single_char
union all
select i+1 as i,convert(varchar(1),substring(#string,i+1,i+1)) as single_char from cte where len(convert(varchar(1),substring(#string,i+1,i+1)))=1
)
select single_char From cte
Related
I tried finding the position of a string with charindex function but when there are two letters in the same string, I'm not able to get the code.
My question is:
Find the postion of S from 'SyedSohail'
The output should be
Position
1
5
Could you please help me with the above
I'm writing the code in Tsql
You can paste the following query right inside your SQL Editor:
DECLARE #test AS varchar(100);
DECLARE #ctr as int;
DECLARE #testlength as int;
DECLARE #charToTest as char(1);
DECLARE #positions as varchar(MAX);
DECLARE #findChar as char(1);
SET #test = 'Syed Summers';
SET #ctr = 1;
SET #testlength = LEN(#test) + 1;
SET #positions = '';
SET #findChar = 'S';
WHILE #ctr < (#testlength)
BEGIN
SET #charToTest = SUBSTRING(#test, #ctr, 1)
IF (UPPER(#charToTest) = #findChar)
BEGIN
SET #positions = #positions + ',' + CONVERT(VARCHAR(10), #ctr)
END
SET #ctr = #ctr + 1
END
SELECT RIGHT(#positions, (LEN(#positions) - 1));
Explanation:
#test - will contain the string which you want to search
#ctr - counter to iterate through all the characters in your string #test
#testLength - length of your #test string
#findChar - the character which you want to count the instance in the string
#charToTest - the string being tested whether it equals your #findChar value
#positions - will be an enumerated string of the position where the #findChar was found in the #test string.
Note that I provided a simplistic answer to help guide the understanding for the person who posted the question.
Try to create a function like below,
CREATE FUNCTION dbo.FindPatternLocation
(
#string NVARCHAR(MAX),
#term NVARCHAR(255)
)
RETURNS TABLE
AS
RETURN
(
SELECT pos = Number - LEN(#term)
FROM (SELECT Number, Item = LTRIM(RTRIM(SUBSTRING(#string, Number,
CHARINDEX(#term, #string + #term, Number) - Number)))
FROM (SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects) AS n(Number)
WHERE Number > 1 AND Number <= CONVERT(INT, LEN(#string)+1)
AND SUBSTRING(#term + #string, Number, LEN(#term)) = #term
) AS y);
Usage:
DECLARE #str VARCHAR(MAX) = 'SyedSohail'
SELECT pos FROM dbo.FindPatternLocation(#str, 's');
Source
If I understand correctly, you could use an rCTE to iterate through the string:
DECLARE #YourString varchar(12) = 'SyedSohail';
DECLARE #Char char(1) = 'S';
WITH rCTE AS(
SELECT V.YourString,
CI.I
FROM (VALUES(#YourString))V(YourString)
CROSS APPLY (VALUES(CHARINDEX(#Char,V.YourString))) CI(I)
WHERE CI.I > 0
UNION ALL
SELECT r.YourString,
CI.I
FROM rCTe r
CROSS APPLY (VALUES(CHARINDEX(#Char,r.YourString,r.I+1))) CI(I)
WHERE CI.I > 0)
SELECT R.I
FROM rCTE r;
However, I suspect (know for a fact), a Tally would be quicker with a dataset and/or longer values:
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.YourString)) FROM (VALUES(#YourString))V(YourString)) --Should be from your tablt
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3, N N4) --10,000 rows
SELECT T.I
FROM Tally T
CROSS JOIN (VALUES(#YourString))V(YourString)
WHERE SUBSTRING(V.YourString,T.I,1) = #Char;
Trying to write a query for printing each character of a string separately. I have tried the following
select substring('sas',1,1)
union all
select substring('sas',2,1)
union all
select substring('sas',3,1)
But I would have to run union all for each character. Any better approach to this?
Sandbox: http://sqlfiddle.com/#!9/9eecb/123683
DECLARE #data VARCHAR(100) = 'October 11, 2017'
;WITH CTE AS
(
SELECT STUFF(#data,1,1,'') TXT, LEFT(#data,1) Col1
UNION ALL
SELECT STUFF(TXT,1,1,'') TXT, LEFT(TXT,1) Col1 FROM CTE
WHERE LEN(TXT) > 0
)
select Col1, ISNUMERIC(Col1) from CTE
You can try this as well
DECLARE #data VARCHAR(100) = 'TEST'
Declare #cnt int = len(#data)
Declare #i int =1
While (#i <= #cnt)
BEGIN
PRint SUBSTRING(#data,#i,1)
set #i=#i+1
END
I really don't like the use of an rCTE for tasks like this, that are iterative and slow (far slower than a Tally, especially when more than a few rows). You could use a Tally and do this far faster. As a TVF, this would like like this:
CREATE FUNCTION dbo.GetChars (#String varchar(8000))
RETURNS table
AS RETURN
WITH N AS(
SELECT N
FROM(VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT TOP (LEN(#String)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3, N N4)
SELECT SUBSTRING(#String, T.I, 1) AS C, T.I
FROM Tally T;
GO
db<>fiddle
Note, this will not work on SQL Server 2005.
Using tsql I want to count a numeric chars in string. For example i've got 'kick0my234ass' string and i wanna count how many (4 in that example) numbers are in that string. I can't use regex, just plain tslq.
You COULD do this I suppose:
declare #c varchar(30)
set #c = 'kick0my234ass'
select #c, len(replace(#c,' ','')) - len(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(#c,'0',''),'1',''),'2',''),'3',''),'4',''),'5',''),'6',''),'7',''),'8',''),'9',''),' ',''))
You'll first have to split the character string in its individual characters, evaluate which are numeric, and finally count those that are. This will do the trick:
DECLARE #test TABLE (Example NVARCHAR(255))
INSERT #test
VALUES ('kick0my234ass')
SELECT COUNT(1)
FROM #test AS T
INNER JOIN master..spt_values v
ON v.type = 'P'
AND v.number < len(T.Example)
WHERE SUBSTRING(T.Example, v.number + 1, 1) LIKE '[0-9]'
You could try this solution with regular expressions (if you'd allow them):
it uses recursive CTE, at every recursive step, one digit is removed from given string and the condition is to stop, when there are no digits in string. The rows are also numbered with consecutive ids, so the last id is the amount of removed digits from string.
declare #str varchar(100) = 'kick0my123ass';
with cte as (
select 1 [id], stuff(#str,PATINDEX('%[0-9]%', #str),1,'') [col]
union all
select [id] + 1, stuff([col],PATINDEX('%[0-9]%', [col]),1,'') from cte
where col like '%[0-9]%'
)
--this will give you number of digits in string
select top 1 id from cte order by id desc
Use a WHILE loop to each each character is a numeric or not.
Query
declare #text as varchar(max) = 'kick0my234ass';
declare #len as int;
select #len = len(#text);
if(#len > 0)
begin
declare #i as int = 1;
declare #count as int = 0;
while(#i <= #len)
begin
if(substring(#text, #i, 1) like '[0-9]')
set #count += 1;
set #i += 1;
end
print 'Count of Numerics in ' + #text + ' : ' + cast(#count as varchar(100));
end
else
print 'Empty string';
If simplicity & performance are important I suggest a purely set-based solution. Grab a copy of DigitsOnlyEE which will remove all non-numeric characters. Then use LEN against the output.
DECLARE #string varchar(100) = '123xxx45ff678';
SELECT string = #string, digitsOnly, DigitCount = LEN(digitsOnly)
FROM dbo.DigitsOnlyEE(#string);
Results
string digitsOnly DigitCount
------------------ ----------- ------------
123xxx45ff678 12345678 8
using a Tally Table created by an rCTE:
CREATE TABLE #Sample (S varchar(100));
INSERT INTO #Sample
VALUES ('kick0my234 ass');
GO
WITH Tally AS(
SELECT 1 AS N
UNION ALL
SELECT N + 1
FROM Tally
WHERE N + 1 <= 100)
SELECT S.S, SUM(CASE WHEN SUBSTRING(S,T.N, 1) LIKE '[0-9]' THEN 1 ELSE 0 END) AS Numbers
FROM #Sample S
JOIN Tally T ON LEN(S.S) >= T.N
GROUP BY S.S;
For future reference, also post your owns attempts please. We aren't here (really) to do your work for you.
Is there a way to replace characters in SQL Server from a string using a mapping table and without using a loop.
I have mapping that can go like this:
a => b
b => c
...
z => a
This mapping is not static and can change.
I tried the solution from https://stackoverflow.com/a/45202933/3161817 and https://stackoverflow.com/a/13051989/3161817 but I only end up having a string that are just a, like 'aaaaaaaa'
My current solution is like:
DECLARE #NextChar NCHAR(1)
DECLARE #Position int = 1
DECLARE #StrLength int = LEN(#str)
DECLARE #Result nvarchar(1000) = ''
WHILE (#Position <= #StrLength)
BEGIN
SET #NextChar = SUBSTRING(#str, #Position, 1)
SET #Result = #Result + ISNULL((SELECT ToChar FROM CharMapping
WHERE #NextChar COLLATE Latin1_General_BIN = FromChar COLLATE Latin1_General_BIN
), #NextChar)
SET #Position= #Position + 1
END
but I'm looking for a possible solution without a loop.
DECLARE #t TABLE(
src char
,dest char
)
INSERT INTO #t VALUES
('a', 'b')
,('b', 'c')
,('d', 'e')
DECLARE #TestString nvarchar(100) = 'aabbcdacbezzz';
WITH cte AS(
SELECT 1 lvl, SUBSTRING(#TestString, 1, 1) AS TestPosChar, SUBSTRING(#TestString, 2, LEN(#TestString)-1) AS TestStringRemain
UNION ALL
SELECT lvl + 1, SUBSTRING(TestStringRemain, 1, 1), SUBSTRING(TestStringRemain, 2, LEN(TestStringRemain)-1)
FROM cte
WHERE LEN(TestStringRemain) >= 1
)
SELECT #TestString AS OldString
,SUBSTRING((SELECT ( '' + ISNULL(t.dest, TestPosChar))
FROM cte c
LEFT JOIN #t AS t ON t.src = c.TestPosChar
ORDER BY lvl
FOR XML PATH( '' )
), 1, 1000 ) AS NewString
I made this test :
declare #MyTab table(
letter char
)
declare #MyTab2 table(
letter char
)
insert into #MyTab
select substring(a.b, v.number+1, 1)
from (select 'ABCDEFGHZZZ' b) a
join master..spt_values v on v.number < len(a.b)
where v.type = 'P'
insert into #MyTab2
select NewLetter
from (
select case letter when 'Z' then 'A'
when 'z' then 'a'
else char(ascii(letter)+1) end NewLetter
from #MyTab
) MyView
select stuff(
(select ''+letter from #MyTab2
for xml path('')),1,0,'')
SQL Server 2017 introduces a TRANSLATE function, which is similar to nested REPLACE functions. You didn't specify what version of SQL Server you are using so I don't know if that's an option.
SELECT TRANSLATE(#SourceString, 'abcdefghijklmnopqrstuvwxyz', 'bcdefghijklmnopqrstuvwxyza');
Try (and expand) following query, which use XML PATH, a tally number table and a decode table:
CREATE TABLE #TTMMPP (ORIG CHAR(1), NEWC CHAR(1));
/* add all values to shift */
INSERT INTO #TTMMPP VALUES ('a','b'),('b','c'),('c','d'),('d','e'),('e','f') /*, ....*/
;
/* N as max len of your string */
CREATE TABLE #TTMMPP2 (N smallint);
DECLARE #I INT
DECLARE #ROWS INT
SET #I = 1
SET #ROWS = 1000
WHILE #I < #ROWS
BEGIN
INSERT INTO #TTMMPP2 VALUES (#I)
SET #I = #I + 1
END
----------------------------------------
DECLARE #my_str VARCHAR(100) = 'abcd';
SELECT #my_str AS ORIGINAL,
(
SELECT ''+C.NEWC
FROM (
SELECT N, SUBSTRING( #my_str, N,1) AS X, B.NEWC
FROM #TTMMPP2 A
INNER JOIN #TTMMPP B ON SUBSTRING(#my_str,A.N,1)= B.ORIG
WHERE N<=LEN(#my_str)
) C
FOR XML PATH('')
) AS SHIFTED;
Output:
ORIGINAL SHIFTED
abcd bcde
Updated version: if you want "mark" character not found in decode table you can use this (little changes to query: LEFT JOIN and COALESCE):
DECLARE #my_str VARCHAR(100) = 'abcdefg';
SELECT #my_str AS ORIGINAL,
(
SELECT ''+C.NEWC
FROM (
SELECT N, SUBSTRING( #my_str, N,1) AS X, COALESCE(B.NEWC,'*') AS NEWC
FROM #TTMMPP2 A
LEFT JOIN #TTMMPP B ON SUBSTRING(#my_str,A.N,1)= B.ORIG
WHERE N<=LEN(#my_str)
) C
ORDER BY N
FOR XML PATH('')
) AS SHIFTED;
Output (* substitute character not found in decode table):
ORIGINAL SHIFTED
abcdefg bcde***
New update (as your last comment added):
SELECT #my_str AS ORIGINAL,
(
SELECT ''+C.NEWC
FROM (
SELECT N, SUBSTRING( #my_str, N,1) AS X, COALESCE(B.NEWC,SUBSTRING(#my_str,A.N,1)) AS NEWC
FROM ##TTMMPP2 A
LEFT JOIN #TTMMPP B ON SUBSTRING(#my_str,A.N,1) COLLATE Latin1_General_BIN = B.ORIG COLLATE Latin1_General_BIN
WHERE N<=LEN(#my_str)
) C
ORDER BY N
FOR XML PATH('')
) AS SHIFTED
Output:
ORIGINAL SHIFTED
abcdefgA bcdeefgA
Please see the SQL statement below:
select * from person1
inner join person2 on person1.reference=person2.reference
where replace(person1.surname,' ','')<>replace(person2.surname,' ','')
I want to join on reference and then list all persons with a different surname in person1 and person2. However, I do not want whitespaces and certain other characters to be used in the matching, but I don't want lots of nested Replace statements like this:
replace(replace(replace(person1.surname,' ',''),char(39),''),'-','')<>replace(replace(replace(person2.surname,' ',''),char(39),''), '-','')
I am trying to design an SQL statements that replaces all characters that are not in the following list as a zero length string:
A-Z
a-z
Hyphen
I believe I could get around this using regular expressions.
Like I said, this will be about as fast as tree growth, but have fun...
CREATE FUNCTION dbo.StripBadCharacters
(
#input NVARCHAR(255)
)
RETURNS NVARCHAR(255)
WITH SCHEMABINDING
AS
BEGIN
DECLARE #s NVARCHAR(255), #i INT;
SELECT #s = N'', #i = 0;
WHILE #i <= LEN(#input)
BEGIN
IF SUBSTRING(#input, #i, 1) LIKE N'[A-Za-z-]'
BEGIN
SET #s = #s + SUBSTRING(#input, #i, 1);
END
SET #i = #i + 1;
END
RETURN (#s);
END
GO
Sample usage:
DECLARE #x TABLE(name1 NVARCHAR(255), name2 NVARCHAR(255));
INSERT #x VALUES('bob o''brien', 'bob obrien'); -- this will return
INSERT #x VALUES('bob obrien', 'bob o '' brien'); -- this will return
INSERT #x VALUES('bob o''brien', 'bob o''brian'); -- this will not
SELECT name1, name2 FROM #x
WHERE dbo.StripBadCharacters(name1) = dbo.StripBadCharacters(name2);
An inline table-valued function. It's relatively snappy.
CREATE FUNCTION dbo.StringCompareAlpha(
#str1 nvarchar(255),
#str2 nvarchar(255)
)
RETURNS TABLE AS
RETURN
(
WITH
t0 AS (SELECT 0 i UNION ALL SELECT 0),
t1 AS (SELECT 0 i FROM t0 a, t0 b),
t2 AS (SELECT 0 i FROM t1 a, t1 b),
t3 AS (SELECT 0 i FROM t2 a, t2 b),
n AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 0)) i FROM t3),
s1 AS (SELECT ROW_NUMBER() OVER(ORDER BY i) i, SUBSTRING(#str1,i,1) c FROM n WHERE SUBSTRING(#str1,i,1) LIKE '[A-Za-z-]' AND i <= LEN(#str1)),
s2 AS (SELECT ROW_NUMBER() OVER(ORDER BY i) i, SUBSTRING(#str2,i,1) c FROM n WHERE SUBSTRING(#str2,i,1) LIKE '[A-Za-z-]' AND i <= LEN(#str2))
SELECT 1 i WHERE NOT EXISTS(
(SELECT * FROM s1 EXCEPT SELECT * FROM s2)
UNION ALL
(SELECT * FROM s2 EXCEPT SELECT * FROM s1)
)
)
GO
DECLARE #x TABLE(name1 NVARCHAR(255), name2 NVARCHAR(255));
INSERT #x VALUES('bob o''brien', 'bob obrien'); -- this will return
INSERT #x VALUES('bob obrien', 'bob o '' brien'); -- this will return
INSERT #x VALUES('bob o''brien', 'bob o''brian'); -- this will not
SELECT * FROM #x CROSS APPLY dbo.StringCompareAlpha(name1,name2)