Extract numbers from a text in SQL Server - sql

i was searching script to extract number from text in sql server and i found this
CREATE FUNCTION [dbo].[GetNumbersFromText](#String VARCHAR(2000))
RETURNS #Number TABLE (Number INT)
AS
BEGIN
DECLARE #Count INT
DECLARE #IntNumbers VARCHAR(1000)
SET #Count = 0
SET #IntNumbers = ''
WHILE #Count <= LEN(#String)
BEGIN
--Find a numeric charactor
IF SUBSTRING(#String,#Count,1) >= '0' AND SUBSTRING(#String,#Count,1) <= '9'
BEGIN
SET #IntNumbers = #IntNumbers + SUBSTRING(#String,#Count,1)
END
--If the next charactor is not a numeric one, the current number ends, so add a separator
IF (SUBSTRING(#String,#Count+1,1) < '0'OR SUBSTRING(#String,#Count+1,1) > '9') AND SUBSTRING(#String,#Count,1) >= '0' AND SUBSTRING(#String,#Count,1) <= '9'
BEGIN
SET #IntNumbers = #IntNumbers + ','
END
SET #Count = #Count + 1
END
---Split string to give a table with the numbers in the text
INSERT INTO #Number
SELECT DISTINCT items FROM dbo.Split(#IntNumbers, ',')
return
END
and call it like
SELECT Number FROM Dbo.[GetNumbersFromText]('Give me 120 this week and 50 next week')
it works fine but i need more short code. can i use patindex to extract number from text.
please anyone share small & good logic to do so. thanks

This is a bit shorter. Turned it into Inline Table Function that uses a recursive CTE to find the numbers.
create function [dbo].[GetNumbersFromText](#String varchar(2000))
returns table as return
(
with C as
(
select cast(substring(S.Value, S1.Pos, S2.L) as int) as Number,
stuff(s.Value, 1, S1.Pos + S2.L, '') as Value
from (select #String+' ') as S(Value)
cross apply (select patindex('%[0-9]%', S.Value)) as S1(Pos)
cross apply (select patindex('%[^0-9]%', stuff(S.Value, 1, S1.Pos, ''))) as S2(L)
union all
select cast(substring(S.Value, S1.Pos, S2.L) as int),
stuff(S.Value, 1, S1.Pos + S2.L, '')
from C as S
cross apply (select patindex('%[0-9]%', S.Value)) as S1(Pos)
cross apply (select patindex('%[^0-9]%', stuff(S.Value, 1, S1.Pos, ''))) as S2(L)
where patindex('%[0-9]%', S.Value) > 0
)
select Number
from C
)
If you expect to have more than 100 numbers in the string you need to call it with option (maxrecursion 0).
declare #S varchar(max)
set #S = 'Give me 120 this week and 50 next week'
select number from GetNumbersFromText(#S) option (maxrecursion 0)

#Vikram's basic idea is not bad, but their query would return all the numbers as a single item. The following function returns a table containing separate numbers as found in the source string:
CREATE FUNCTION dbo.GetNumbersFromText (#String varchar(2000))
RETURNS TABLE
AS
RETURN (
WITH NumbersSplit AS (
SELECT
C = SUBSTRING(#String, number, 1),
i = number,
g = number - ROW_NUMBER() OVER (ORDER BY number)
FROM master..spt_values
WHERE type = 'P'
AND SUBSTRING(#String, number, 1) BETWEEN '0' AND '9'
),
NumbersAssembled AS (
SELECT
number = CAST(
(SELECT C + '' FROM NumbersSplit WHERE g = g.g ORDER BY i FOR XML PATH (''))
AS varchar(2000)
)
FROM NumbersSplit g
GROUP BY g
)
SELECT * FROM NumbersAssembled
)
Note: this solution would work in SQL Server 2005 or later.

try the following logic:
declare #thestring varchar(50)
set #thestring = 'Give me 120 this week and 50 next week'
declare #final varchar(50)
set #final = ''
select #final = #final + x.thenum
from
(
select substring(#thestring, number, 1) as thenum, number
from master..spt_values
where substring(#thestring, number, 1) like '[0-9]' and type='P'
) x
order by x.number
print #final

-- Try This Code...
-- Its OK!!!
CREATE FUNCTION [dbo].[udf_ExtractNumberFromString]
(
#pInputString VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #OutputString varchar(MAX)=''
DECLARE #string varchar(MAX)
DECLARE #start INT
DECLARE #end INT
DECLARE #len INT
SET #string=#pInputString
--SET #string = 'the22478ddffafghrty12345TestAddressdd5aa789324-#345'
SET #string = replace(#string, ' ' , '')
WHILE PATINDEX('%[0-9]%',#string) <> 0
BEGIN
SET #len = len(#string)
-- PRINT #len
set #start = PATINDEX('%[0-9]%',#string)
-- PRINT #start
SET #end= PATINDEX('%[^0-9]%',SUBSTRING(#string,#start,#len-#start))
-- PRINT #end
IF #end=0
BEGIN
SET #end=#len-#start
SET #OutputString=SUBSTRING(#string,#start,#end+1)+'-'+#OutputString
BREAK
END
ELSE
BEGIN
SET #OutputString=SUBSTRING(#string,#start,#end-1)+'-'+#OutputString
SET #string=SUBSTRING(#string,#end+#start-1,#len-#end)
END
--PRINT #string
--PRINT #Output
--PRINT '---------------------'
END
IF LEN(#OutputString)>0
SET #OutputString=LEFT(#OutputString,LEN(#OutputString)-1)
--PRINT #OutputString
RETURN #OutputString
END

Here is an appropriate and working solution for this query
http://www.ittutorials.in/source/sql/sql-function-to-extract-only-numbers-from-string.aspx

Related

SQL Server capitalize every first letter of every word with loop [duplicate]

What’s the best way to capitalize the first letter of each word in a string in SQL Server.
From http://www.sql-server-helper.com/functions/initcap.aspx
CREATE FUNCTION [dbo].[InitCap] ( #InputString varchar(4000) )
RETURNS VARCHAR(4000)
AS
BEGIN
DECLARE #Index INT
DECLARE #Char CHAR(1)
DECLARE #PrevChar CHAR(1)
DECLARE #OutputString VARCHAR(255)
SET #OutputString = LOWER(#InputString)
SET #Index = 1
WHILE #Index <= LEN(#InputString)
BEGIN
SET #Char = SUBSTRING(#InputString, #Index, 1)
SET #PrevChar = CASE WHEN #Index = 1 THEN ' '
ELSE SUBSTRING(#InputString, #Index - 1, 1)
END
IF #PrevChar IN (' ', ';', ':', '!', '?', ',', '.', '_', '-', '/', '&', '''', '(')
BEGIN
IF #PrevChar != '''' OR UPPER(#Char) != 'S'
SET #OutputString = STUFF(#OutputString, #Index, 1, UPPER(#Char))
END
SET #Index = #Index + 1
END
RETURN #OutputString
END
GO
There is a simpler/smaller one here (but doesn't work if any row doesn't have spaces, "Invalid length parameter passed to the RIGHT function."):
http://www.devx.com/tips/Tip/17608
As a table-valued function:
CREATE FUNCTION dbo.InitCap(#v AS VARCHAR(MAX))
RETURNS TABLE
AS
RETURN
WITH a AS (
SELECT (
SELECT UPPER(LEFT(value, 1)) + LOWER(SUBSTRING(value, 2, LEN(value))) AS 'data()'
FROM string_split(#v, ' ')
ORDER BY CHARINDEX(value,#v)
FOR XML PATH (''), TYPE) ret)
SELECT CAST(a.ret AS varchar(MAX)) ret from a
GO
Note that string_split requires COMPATIBILITY_LEVEL 130.
A variation of the one I've been using for quite some time is:
CREATE FUNCTION [widget].[properCase](#string varchar(8000)) RETURNS varchar(8000) AS
BEGIN
SET #string = LOWER(#string)
DECLARE #i INT
SET #i = ASCII('a')
WHILE #i <= ASCII('z')
BEGIN
SET #string = REPLACE( #string, ' ' + CHAR(#i), ' ' + CHAR(#i-32))
SET #i = #i + 1
END
SET #string = CHAR(ASCII(LEFT(#string, 1))-32) + RIGHT(#string, LEN(#string)-1)
RETURN #string
END
You can easily modify to handle characters after items other than spaces if you wanted to.
Another solution without using the loop - pure set-based approach with recursive CTE
create function [dbo].InitCap (#value varchar(max))
returns varchar(max) as
begin
declare
#separator char(1) = ' ',
#result varchar(max) = '';
with r as (
select value, cast(null as varchar(max)) [x], cast('' as varchar(max)) [char], 0 [no] from (select rtrim(cast(#value as varchar(max))) [value]) as j
union all
select right(value, len(value)-case charindex(#separator, value) when 0 then len(value) else charindex(#separator, value) end) [value]
, left(r.[value], case charindex(#separator, r.value) when 0 then len(r.value) else abs(charindex(#separator, r.[value])-1) end ) [x]
, left(r.[value], 1)
, [no] + 1 [no]
from r where value > '')
select #result = #result +
case
when ascii([char]) between 97 and 122
then stuff(x, 1, 1, char(ascii([char])-32))
else x
end + #separator
from r where x is not null;
set #result = rtrim(#result);
return #result;
end
If you are looking for the answer to the same question in Oracle/PLSQL then you may use the function INITCAP. Below is an example for the attribute dname from a table department which has the values ('sales', 'management', 'production', 'development').
SQL> select INITCAP(dname) from department;
INITCAP(DNAME)
--------------------------------------------------
Sales
Management
Production
Development
;WITH StudentList(Name) AS (
SELECT CONVERT(varchar(50), 'Carl-VAN')
UNION SELECT 'Dean o''brian'
UNION SELECT 'Andrew-le-Smith'
UNION SELECT 'Eddy thompson'
UNION SELECT 'BOBs-your-Uncle'
), Student AS (
SELECT CONVERT(varchar(50), UPPER(LEFT(Name, 1)) + LOWER(SUBSTRING(Name, 2, LEN(Name)))) Name,
pos = PATINDEX('%[-'' ]%', Name)
FROM StudentList
UNION ALL
SELECT CONVERT(varchar(50), LEFT(Name, pos) + UPPER(SUBSTRING(Name, pos + 1, 1)) + SUBSTRING(Name, pos + 2, LEN(Name))) Name,
pos = CASE WHEN PATINDEX('%[-'' ]%', RIGHT(Name, LEN(Name) - pos)) = 0 THEN 0 ELSE pos + PATINDEX('%[-'' ]%', RIGHT(Name, LEN(Name) - pos)) END
FROM Student
WHERE pos > 0
)
SELECT Name
FROM Student
WHERE pos = 0
ORDER BY Name
This will result in:
Andrew-Le-Smith
Bobs-Your-Uncle
Carl-Van
Dean O'Brian
Eddy Thompson
Using a recursive CTE set based query should out perform a procedural while loop query.
Here I also have made my separate to be 3 different characters [-' ] instead of 1 for a more advanced example. Using PATINDEX as I have done allows me to look for many characters. You could also use CHARINDEX on a single character and this function excepts a third parameter StartFromPosition so I could further simply my 2nd part of the recursion of the pos formula to (assuming a space): pos = CHARINDEX(' ', Name, pos + 1).
The suggested function works fine, however, if you do not want to create any function this is how I do it:
select ID,Name
,string_agg(concat(upper(substring(value,1,1)),lower(substring(value,2,len(value)-1))),' ') as ModifiedName
from Table_Customer
cross apply String_Split(replace(trim(Name),' ',' '),' ')
where Name is not null
group by ID,Name;
The above query split the words by space (' ') and create different rows of each having one substring, then convert the first letter of each substring to upper and keep remaining as lower. The final step is to string aggregate based on the key.
BEGIN
DECLARE #string varchar(100) = 'asdsadsd asdad asd'
DECLARE #ResultString varchar(200) = ''
DECLARE #index int = 1
DECLARE #flag bit = 0
DECLARE #temp varchar(2) = ''
WHILE (#Index <LEN(#string)+1)
BEGIN
SET #temp = SUBSTRING(#string, #Index-1, 1)
--select #temp
IF #temp = ' ' OR #index = 1
BEGIN
SET #ResultString = #ResultString + UPPER(SUBSTRING(#string, #Index, 1))
END
ELSE
BEGIN
SET #ResultString = #ResultString + LOWER(SUBSTRING(#string, #Index, 1))
END
SET #Index = #Index+ 1--increase the index
END
SELECT #ResultString
END
It can be as simple as this:
DECLARE #Name VARCHAR(500) = 'Roger';
SELECT #Name AS Name, UPPER(LEFT(#Name, 1)) + SUBSTRING(#Name, 2, LEN(#Name)) AS CapitalizedName;
fname is column name if fname value is akhil then UPPER(left(fname,1)) provide capital First letter(A) and substring function SUBSTRING(fname,2,LEN(fname)) provide(khil) concate both using + then result is (Akhil)
select UPPER(left(fname,1))+SUBSTRING(fname,2,LEN(fname)) as fname
FROM [dbo].[akhil]
On SQL Server 2016+ using JSON which gives guaranteed order of the words:
CREATE FUNCTION [dbo].[InitCap](#Text NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN STUFF((
SELECT ' ' + UPPER(LEFT(s.value,1)) + LOWER(SUBSTRING(s.value,2,LEN(s.value)))
FROM OPENJSON('["' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(#Text,'\','\\'),'"','\"'),CHAR(9),'\t'),CHAR(10),'\n'),' ','","') + '"]') s
ORDER BY s.[key]
FOR XML PATH(''),TYPE).value('(./text())[1]','NVARCHAR(MAX)'),1,1,'');
END
GO
CREATE FUNCTION [dbo].[Capitalize](#text NVARCHAR(MAX)) RETURNS NVARCHAR(MAX) AS
BEGIN
DECLARE #result NVARCHAR(MAX) = '';
DECLARE #c NVARCHAR(1);
DECLARE #i INT = 1;
DECLARE #isPrevSpace BIT = 1;
WHILE #i <= LEN(#text)
BEGIN
SET #c = SUBSTRING(#text, #i, 1);
SET #result += IIF(#isPrevSpace = 1, UPPER(#c), LOWER(#c));
SET #isPrevSpace = IIF(#c LIKE '[ -]', 1, 0);
SET #i += 1;
END
RETURN #result;
END
GO
DECLARE #sentence NVARCHAR(100) = N'i-thINK-this soLUTION-works-LiKe-a charm';
PRINT dbo.Capitalize(#sentence);
-- I-Think-This Solution-Works-Like-A Charm
Here is the simplest one-liner to do this:
SELECT LEFT(column, 1)+ lower(RIGHT(column, len(column)-1) ) FROM [tablename]
I was looking for the best way to capitalize and i recreate simple sql script
how to use SELECT dbo.Capitalyze('this is a test with multiple spaces')
result "This Is A Test With Multiple Spaces"
CREATE FUNCTION Capitalyze(#input varchar(100) )
returns varchar(100)
as
begin
declare #index int=0
declare #char as varchar(1)=' '
declare #prevCharIsSpace as bit=1
declare #Result as varchar(100)=''
set #input=UPPER(LEFT(#input,1))+LOWER(SUBSTRING(#input, 2, LEN(#input)))
set #index=PATINDEX('% _%',#input)
if #index=0
set #index=len(#input)
set #Result=substring(#input,0,#index+1)
WHILE (#index < len(#input))
BEGIN
SET #index = #index + 1
SET #char=substring(#input,#index,1)
if (#prevCharIsSpace=1)
begin
set #char=UPPER(#char)
if (#char=' ')
set #char=''
end
if (#char=' ')
set #prevCharIsSpace=1
else
set #prevCharIsSpace=0
set #Result=#Result+#char
--print #Result
END
--print #Result
return #Result
end
IF OBJECT_ID ('dbo.fnCapitalizeFirstLetterAndChangeDelimiter') IS NOT NULL
DROP FUNCTION dbo.fnCapitalizeFirstLetterAndChangeDelimiter
GO
CREATE FUNCTION [dbo].[fnCapitalizeFirstLetterAndChangeDelimiter] (#string NVARCHAR(MAX), #delimiter NCHAR(1), #new_delimeter NCHAR(1))
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #result NVARCHAR(MAX)
SELECT #result = '';
IF (LEN(#string) > 0)
DECLARE #curr INT
DECLARE #next INT
BEGIN
SELECT #curr = 1
SELECT #next = CHARINDEX(#delimiter, #string)
WHILE (LEN(#string) > 0)
BEGIN
SELECT #result =
#result +
CASE WHEN LEN(#result) > 0 THEN #new_delimeter ELSE '' END +
UPPER(SUBSTRING(#string, #curr, 1)) +
CASE
WHEN #next <> 0
THEN LOWER(SUBSTRING(#string, #curr+1, #next-2))
ELSE LOWER(SUBSTRING(#string, #curr+1, LEN(#string)-#curr))
END
IF (#next > 0)
BEGIN
SELECT #string = SUBSTRING(#string, #next+1, LEN(#string)-#next)
SELECT #next = CHARINDEX(#delimiter, #string)
END
ELSE
SELECT #string = ''
END
END
RETURN #result
END
GO

Reverse only numerical parts of string in sql server

With T-SQL, I'm trying to find the easiest way to reverse numbers in string. so for string like Test123Hello have Test321Hello.
[Before] [After]
Test123Hello Test321Hello
Tt143 Hello Tt341 Hello
12Hll 21Hll
Tt123H3451end Tt321H1543end
you can use this function
CREATE FUNCTION [dbo].[fn_ReverseDigit_MA]
(
#Str_IN nVARCHAR(max)
)
RETURNS NVARCHAR(max)
AS
BEGIN
DECLARE #lenstr AS INT =LEN(#Str_IN)
DECLARE #lastdigend AS INT=0
while (#lastdigend<#lenstr)
BEGIN
DECLARE #strPart1 AS NVARCHAR(MAX)=LEFT(#Str_IN,#lastdigend)
declare #lenstrPart1 AS INT=LEN(#strPart1)
DECLARE #strPart2 AS NVARCHAR(MAX)=RIGHT(#Str_IN,#lenstr-#lastdigend)
declare #digidx as int=patindex(N'%[0-9]%' ,#strPart2)+#lenstrPart1
IF(#digidx=#lenstrPart1)
BEGIN
BREAK;
END
DECLARE #strStartdig AS NVARCHAR(MAX) = RIGHT(#Str_IN,#lenstr-#digidx+1)
declare #NDidx as int=patindex(N'%[^0-9]%' ,#strStartdig)+#digidx-1
IF(#NDidx<=#digidx)
BEGIN
SET #NDidx=#lenstr+1
END
DECLARE #strRet AS NVARCHAR(MAX)=LEFT(#Str_IN,#digidx-1) +REVERSE(SUBSTRING(#Str_IN,#digidx,#NDidx-#digidx)) +RIGHT(#Str_IN,#lenstr-#NDidx+1)
SET #Str_IN=#strRet
SET #lastdigend=#NDidx-1
END
return #Str_IN
END
Just make use of PATINDEX for searching, append to the result string part by part:
CREATE FUNCTION [dbo].[fn_ReverseDigits]
(
#Value nvarchar(max)
)
RETURNS NVARCHAR(max)
AS
BEGIN
IF #Value IS NULL
RETURN NULL
DECLARE
#TextIndex int = PATINDEX('%[^0-9]%', #Value),
#NumIndex int = PATINDEX('%[0-9]%', #Value),
#ResultValue nvarchar(max) = ''
WHILE LEN(#ResultValue) < LEN(#Value)
BEGIN
-- Set the index to end of the string if the index is 0
SELECT #TextIndex = CASE WHEN #TextIndex = 0 THEN LEN(#Value) + 1 ELSE LEN(#ResultValue) + #TextIndex END
SELECT #NumIndex = CASE WHEN #NumIndex = 0 THEN LEN(#Value) + 1 ELSE LEN(#ResultValue) + #NumIndex END
IF #NumIndex < #TextIndex
SELECT #ResultValue = #ResultValue + REVERSE(SUBSTRING(#Value, #NumIndex, #TextIndex -#NumIndex))
ELSE
SELECT #ResultValue = #ResultValue + (SUBSTRING(#Value, #TextIndex, #NumIndex - #TextIndex))
-- Update index variables
SELECT
#TextIndex = PATINDEX('%[^0-9]%', SUBSTRING(#Value, LEN(#ResultValue) + 1, LEN(#Value) - LEN(#ResultValue))),
#NumIndex = PATINDEX('%[0-9]%', SUBSTRING(#Value, LEN(#ResultValue) + 1, LEN(#Value) - LEN(#ResultValue)))
END
RETURN #ResultValue
END
Test SQL
declare #Values table (Value varchar(20))
INSERT #Values VALUES
('Test123Hello'),
('Tt143 Hello'),
('12Hll'),
('Tt123H3451end'),
(''),
(NULL)
SELECT Value, dbo.fn_ReverseDigits(Value) ReversedValue FROM #Values
Result
Value ReversedValue
-------------------- --------------------
Test123Hello Test321Hello
Tt143 Hello Tt341 Hello
12Hll 21Hll
Tt123H3451end Tt321H1543end
NULL NULL
hope this help:
declare #s nvarchar(128) ='Test321Hello'
declare #numStart as int, #numEnd as int
select #numStart =patindex('%[0-9]%',#s)
select #numEnd=len(#s)-patindex('%[0-9]%',REVERSE(#s))
select
SUBSTRING(#s,0,#numstart)+
reverse(SUBSTRING(#s,#numstart,#numend-#numstart+2))+
SUBSTRING(#s,#numend+2,len(#s)-#numend)
Use this function it will handle multiple occurrence of numbers too
create FUNCTION [dbo].[GetReverseNumberFromString] (#String VARCHAR(2000))
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #Count INT
DECLARE #IntNumbers VARCHAR(1000)
declare #returnstring varchar(max)=#String;
SET #Count = 0
SET #IntNumbers = ''
WHILE #Count <= LEN(#String)
BEGIN
IF SUBSTRING(#String, #Count, 1) >= '0'
AND SUBSTRING(#String, #Count, 1) <= '9'
BEGIN
SET #IntNumbers = #IntNumbers + SUBSTRING(#String, #Count, 1)
END
IF (
SUBSTRING(#String, #Count + 1, 1) < '0'
OR SUBSTRING(#String, #Count + 1, 1) > '9'
)
AND SUBSTRING(#String, #Count, 1) >= '0'
AND SUBSTRING(#String, #Count, 1) <= '9'
BEGIN
SET #IntNumbers = #IntNumbers + ','
END
SET #Count = #Count + 1
END
declare #RevStrings table (itemz varchar(50))
INSERT INTO #RevStrings(itemz)
select items from dbo.Split(#IntNumbers,',')
select #returnstring = Replace(#returnstring, itemz,REVERSE(itemz))from #RevStrings
RETURN #returnstring
END
your sample string
select [dbo].[GetReverseNumberFromString]('Tt123H3451end')
result
Tt321H1543end
UPDATE :
if you do not have Split function then first create it
i have included it below
create FUNCTION Split
(
#Input NVARCHAR(MAX),
#Character CHAR(1)
)
RETURNS #Output TABLE (
Items NVARCHAR(1000)
)
AS
BEGIN
DECLARE #StartIndex INT, #EndIndex INT
SET #StartIndex = 1
IF SUBSTRING(#Input, LEN(#Input) - 1, LEN(#Input)) <> #Character
BEGIN
SET #Input = #Input + #Character
END
WHILE CHARINDEX(#Character, #Input) > 0
BEGIN
SET #EndIndex = CHARINDEX(#Character, #Input)
INSERT INTO #Output(Items)
SELECT SUBSTRING(#Input, #StartIndex, #EndIndex - 1)
SET #Input = SUBSTRING(#Input, #EndIndex + 1, LEN(#Input))
END
RETURN
END
GO
This is a set based approach:
;WITH Tally (n) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
), UnpivotCTE AS (
SELECT id, x.c, n, y.isNumber,
n - ROW_NUMBER() OVER (PARTITION BY id, y.isNumber
ORDER BY n) AS grp
FROM mytable
CROSS JOIN Tally
CROSS APPLY (SELECT SUBSTRING(col, n, 1)) AS x(c)
CROSS APPLY (SELECT ISNUMERIC(x.c)) AS y(isNumber)
WHERE n <= LEN(col)
), ToConcatCTE AS (
SELECT id, c, n, isNumber,
grp + MIN(n) OVER (PARTITION BY id, isNumber, grp) AS grpAsc
FROM UnpivotCTE
)
SELECT id, col,
REPLACE(
(SELECT c AS [text()]
FROM ToConcatCTE AS t
WHERE t.id = m.id
ORDER BY id,
grpAsc,
CASE WHEN isNumber = 0 THEN n END,
CASE WHEN isNumber = 1 THEN n END DESC
FOR XML PATH('')), ' ',' ') AS col2
FROM mytable AS m
A tally table is used in order to 'unpivot' all characters of the string. Then ROW_NUMBER is used in order to identify islands of numeric and non-numeric characters. Finally, FOR XML PATH is used to reconstruct the initial string with numerical islands reversed: ORDER BY is used to sort islands of numeric characters in reversed order.
Fiddle Demo here
This would do the specific string you are asking for:
select
substring('Test123Hello',1,4)
+
reverse(substring('Test123Hello',5,3))
+
substring('Test123Hello',8,5)
Judging by the rest of the values it looks like you would need to make templates for any of the alphanumeric patterns you are getting. For example you would apply the above to any values that had the shape:
select * from [B&A] where [before] like '[a-z][a-z][a-z][a-z][0-9][0-9][0-9]
[a-z][a-z][a-z][a-z][a-z]'
In other words, if you put the values (before and after) into a table [B&A] and called the columns 'before' and 'after' then ran this:
select
substring(before,1,4)
+
reverse(substring(before,5,3))
+
substring(before,8,5) as [after]
from [B&A] where [before] like '[a-z][a-z][a-z][a-z][0-9][0-9][0-9][a-z]
[a-z][a-z][a-z][a-z]'
Then it would give you 'Test321Hello'.
However the other 3 rows would not be affected unless you created a similar
'[0-9][a-z]' type template for each alphanumeric shape and applied this to the [B&A] table. You would have to select the results into a temp table or another table.
By applying each template in turn you'd get most of it then you'd have to see how many rows were unaffected and check what the alphanumeric shape is and make more templates. Eventually you have a set of code which, if you ran it would capture all possible combinations.
You could just sit down and design a code in this way which captured all possible combinations of [a-z] and [0-9]. A lot depends on the maximum number of characters you are dealing with.

User Defined Function to get longest word in a string in SQL Server

I've trying to create a UDF in SQL to return the longest word in a string. I've created the following but I cant get it to work properly. Any suggestions?
CREATE FUNCTION [dbo].[ufn_Longestword] (#input varchar(255))
RETURNS varchar(100)
AS
BEGIN
declare #pos int
declare #pos2 int
declare #wordpos int
declare #longestword varchar(100)
declare #Letter1 varchar (1)
declare #Letter2 varchar (1)
declare #twords table (
words varchar(100))
SET #pos = 1
WHILE #pos <= len(#input)
BEGIN
SET #Letter1 = substring(#input, #pos, 1)
IF #Letter1 = ' '
BEGIN
SET #pos2 = #pos
WHILE #pos2 <= len(#input)
BEGIN
SET #Letter2 = substring(#input, #pos2, 1)
if #letter2 = ' '
BEGIN
insert into #twords
select SUBSTRING(#input, #pos,#pos2 - #pos)
END
SET #pos2 = #pos2 + 1
END
END
SET #pos = #pos + 1
END
SET #longestword = (select top 1 words from #twords
ORDER BY len(words)desc)
delete from #twords
RETURN #longestword
END
I#m trying to get the different between the 2 spaces and insert that word into a temp table but it doesnt work.
Instead you can use this.
DECLARE #str VARCHAR(5000)='aaaa bbbbb cccccccc'
SELECT TOP 1 Split.a.value('.', 'VARCHAR(100)') as longest_Word
FROM (SELECT Cast ('<M>' + Replace(#str, ' ', '</M><M>') + '</M>' AS XML) AS Data) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
ORDER BY Len(Split.a.value('.', 'VARCHAR(100)')) DESC
Result : cccccccc
i found this as solution :
click Here
CREATE FUNCTION FN_ex06(#str varchar(8000) )
RETURNS #T TABLE
( position int IDENTITY PRIMARY KEY,
value varchar(8000) ,
length smallint null
)
AS
BEGIN
DECLARE #i int
SET #i = -1
WHILE (LEN(#str) > 0)
BEGIN
SET #i = CHARINDEX(' ' , #str) /* here i used space as delimiter*/
IF (#i = 0) AND (LEN(#str) > 0)
BEGIN
INSERT INTO #T (value, length) VALUES (#str, LEN(#str))
BREAK
END
IF (#i > 1)
BEGIN
INSERT INTO #T (value, length) VALUES (LEFT(#str, #i - 1),
LEN(LEFT(#str, #i - 1)))
SET #str = RIGHT(#str, (LEN(#str) - #i))
END
ELSE
SET #str = RIGHT(#str, (LEN(#str) - #i))
END
RETURN
END
to run this function you treat it as table because return is a table
select max(value) from FN_ex06('karim pentester')
result is : pentester
of course you select all other columns in return table

Append a specific character after each character of a string in sql server

Thanks to advise me for the below issue:
I am using below query to fetch the value of a column:
Select OptionList = case when isnull(AS_CIS_Code,'') <> '' then AS_CIS_Code
else ''
end
from added_services
AS_CIS_Code column is of varchar(10) in added_services table. It contains values like 'AB', 'ABC', 'GHKIK', 'UYTIOPJ' and so on which represents different codes.
Now I have to select these codes after modifying the above query so that '_' is appended after each character.
Like it should be fetched as 'A_B_', 'A_B_C_', 'G_H_K_I_K_', 'U_Y_T_I_O_P_J_'.
How should I implement it? Using a temp table will down the performance for one column only, so should I use while loop or please suggest me better alternatives.
Try this:
DECLARE #Input VARCHAR(100) = 'TESTING'
DECLARE #Pos INT = LEN(#Input)
WHILE #Pos > 1
BEGIN
SET #Input = STUFF(#Input,#Pos,0,'_')
SET #Pos = #Pos - 1
END
SELECT #Input
Output
T_E_S_T_I_N_G
UDF
CREATE FUNCTION PadStr(#Data VARCHAR(100)) RETURNS VARCHAR(200)
AS
BEGIN
DECLARE #Input VARCHAR(200) = #Data
DECLARE #Pos INT = LEN(#Input)
WHILE #Pos > 1
BEGIN
SET #Input = STUFF(#Input,#Pos,0,'_')
SET #Pos = #Pos - 1
END
RETURN #Input + '_'
END
Output
SELECT dbo.PadStr('TESTING') -- T_E_S_T_I_N_G_
You can split the string using a numbers table and the rebuild it using for xml path().
select isnull(C.Value, '') as AS_CIS_Code
from added_services as A
cross apply (
select substring(A.AS_CIS_Code, T.N, 1)+'_'
from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) as T(N)
where T.N <= len(A.AS_CIS_Code)
order by T.N
for xml path('')
) as C(Value)
SQL Fiddle
Try this
Create Function Code_Pad
(
#code varchar(max)
)
returns varchar(max)
as
begin
Declare #coding varchar(max) =''
Declare #i int = 1
WHILE(LEN(#Coding)<=2*len(#code)-1)
begin
Select #coding = #coding+SUBSTRING(#code,#i,1)+'_'
set #i=#i+1
end
return #coding
end
End of Function
Select OptionList = case when isnull(AS_CIS_Code,'') <> '' then dbo.Code_Pad(AS_CIS_Code) else ''
end
from added_services
May not be best solution, but works in one query using recursion
;WITH valCTE(Replaced,ToBeReplaced,Position)
AS
(
SELECT CAST(LEFT(AS_CIS_Code,1) + '_' AS VARCHAR(20))
,SUBSTRING(AS_CIS_Code,2,LEN(AS_CIS_Code)-1)
,1
FROM added_services
UNION ALL
SELECT CAST(Replaced + LEFT(ToBeReplaced,1) + '_' AS VARCHAR(20))
,SUBSTRING(ToBeReplaced,2,LEN(ToBeReplaced)-1)
,Position+1
FROM valCTE
WHERE LEN(ToBeReplaced)>0
)
SELECT TOP 1 LEFT(Replaced,LEN(Replaced)-1) -- remove last _
FROM valCTE
ORDER BY Position DESC

How to capitalize the first letter of each word in a string in SQL Server

What’s the best way to capitalize the first letter of each word in a string in SQL Server.
From http://www.sql-server-helper.com/functions/initcap.aspx
CREATE FUNCTION [dbo].[InitCap] ( #InputString varchar(4000) )
RETURNS VARCHAR(4000)
AS
BEGIN
DECLARE #Index INT
DECLARE #Char CHAR(1)
DECLARE #PrevChar CHAR(1)
DECLARE #OutputString VARCHAR(255)
SET #OutputString = LOWER(#InputString)
SET #Index = 1
WHILE #Index <= LEN(#InputString)
BEGIN
SET #Char = SUBSTRING(#InputString, #Index, 1)
SET #PrevChar = CASE WHEN #Index = 1 THEN ' '
ELSE SUBSTRING(#InputString, #Index - 1, 1)
END
IF #PrevChar IN (' ', ';', ':', '!', '?', ',', '.', '_', '-', '/', '&', '''', '(')
BEGIN
IF #PrevChar != '''' OR UPPER(#Char) != 'S'
SET #OutputString = STUFF(#OutputString, #Index, 1, UPPER(#Char))
END
SET #Index = #Index + 1
END
RETURN #OutputString
END
GO
There is a simpler/smaller one here (but doesn't work if any row doesn't have spaces, "Invalid length parameter passed to the RIGHT function."):
http://www.devx.com/tips/Tip/17608
As a table-valued function:
CREATE FUNCTION dbo.InitCap(#v AS VARCHAR(MAX))
RETURNS TABLE
AS
RETURN
WITH a AS (
SELECT (
SELECT UPPER(LEFT(value, 1)) + LOWER(SUBSTRING(value, 2, LEN(value))) AS 'data()'
FROM string_split(#v, ' ')
ORDER BY CHARINDEX(value,#v)
FOR XML PATH (''), TYPE) ret)
SELECT CAST(a.ret AS varchar(MAX)) ret from a
GO
Note that string_split requires COMPATIBILITY_LEVEL 130.
A variation of the one I've been using for quite some time is:
CREATE FUNCTION [widget].[properCase](#string varchar(8000)) RETURNS varchar(8000) AS
BEGIN
SET #string = LOWER(#string)
DECLARE #i INT
SET #i = ASCII('a')
WHILE #i <= ASCII('z')
BEGIN
SET #string = REPLACE( #string, ' ' + CHAR(#i), ' ' + CHAR(#i-32))
SET #i = #i + 1
END
SET #string = CHAR(ASCII(LEFT(#string, 1))-32) + RIGHT(#string, LEN(#string)-1)
RETURN #string
END
You can easily modify to handle characters after items other than spaces if you wanted to.
Another solution without using the loop - pure set-based approach with recursive CTE
create function [dbo].InitCap (#value varchar(max))
returns varchar(max) as
begin
declare
#separator char(1) = ' ',
#result varchar(max) = '';
with r as (
select value, cast(null as varchar(max)) [x], cast('' as varchar(max)) [char], 0 [no] from (select rtrim(cast(#value as varchar(max))) [value]) as j
union all
select right(value, len(value)-case charindex(#separator, value) when 0 then len(value) else charindex(#separator, value) end) [value]
, left(r.[value], case charindex(#separator, r.value) when 0 then len(r.value) else abs(charindex(#separator, r.[value])-1) end ) [x]
, left(r.[value], 1)
, [no] + 1 [no]
from r where value > '')
select #result = #result +
case
when ascii([char]) between 97 and 122
then stuff(x, 1, 1, char(ascii([char])-32))
else x
end + #separator
from r where x is not null;
set #result = rtrim(#result);
return #result;
end
If you are looking for the answer to the same question in Oracle/PLSQL then you may use the function INITCAP. Below is an example for the attribute dname from a table department which has the values ('sales', 'management', 'production', 'development').
SQL> select INITCAP(dname) from department;
INITCAP(DNAME)
--------------------------------------------------
Sales
Management
Production
Development
;WITH StudentList(Name) AS (
SELECT CONVERT(varchar(50), 'Carl-VAN')
UNION SELECT 'Dean o''brian'
UNION SELECT 'Andrew-le-Smith'
UNION SELECT 'Eddy thompson'
UNION SELECT 'BOBs-your-Uncle'
), Student AS (
SELECT CONVERT(varchar(50), UPPER(LEFT(Name, 1)) + LOWER(SUBSTRING(Name, 2, LEN(Name)))) Name,
pos = PATINDEX('%[-'' ]%', Name)
FROM StudentList
UNION ALL
SELECT CONVERT(varchar(50), LEFT(Name, pos) + UPPER(SUBSTRING(Name, pos + 1, 1)) + SUBSTRING(Name, pos + 2, LEN(Name))) Name,
pos = CASE WHEN PATINDEX('%[-'' ]%', RIGHT(Name, LEN(Name) - pos)) = 0 THEN 0 ELSE pos + PATINDEX('%[-'' ]%', RIGHT(Name, LEN(Name) - pos)) END
FROM Student
WHERE pos > 0
)
SELECT Name
FROM Student
WHERE pos = 0
ORDER BY Name
This will result in:
Andrew-Le-Smith
Bobs-Your-Uncle
Carl-Van
Dean O'Brian
Eddy Thompson
Using a recursive CTE set based query should out perform a procedural while loop query.
Here I also have made my separate to be 3 different characters [-' ] instead of 1 for a more advanced example. Using PATINDEX as I have done allows me to look for many characters. You could also use CHARINDEX on a single character and this function excepts a third parameter StartFromPosition so I could further simply my 2nd part of the recursion of the pos formula to (assuming a space): pos = CHARINDEX(' ', Name, pos + 1).
The suggested function works fine, however, if you do not want to create any function this is how I do it:
select ID,Name
,string_agg(concat(upper(substring(value,1,1)),lower(substring(value,2,len(value)-1))),' ') as ModifiedName
from Table_Customer
cross apply String_Split(replace(trim(Name),' ',' '),' ')
where Name is not null
group by ID,Name;
The above query split the words by space (' ') and create different rows of each having one substring, then convert the first letter of each substring to upper and keep remaining as lower. The final step is to string aggregate based on the key.
BEGIN
DECLARE #string varchar(100) = 'asdsadsd asdad asd'
DECLARE #ResultString varchar(200) = ''
DECLARE #index int = 1
DECLARE #flag bit = 0
DECLARE #temp varchar(2) = ''
WHILE (#Index <LEN(#string)+1)
BEGIN
SET #temp = SUBSTRING(#string, #Index-1, 1)
--select #temp
IF #temp = ' ' OR #index = 1
BEGIN
SET #ResultString = #ResultString + UPPER(SUBSTRING(#string, #Index, 1))
END
ELSE
BEGIN
SET #ResultString = #ResultString + LOWER(SUBSTRING(#string, #Index, 1))
END
SET #Index = #Index+ 1--increase the index
END
SELECT #ResultString
END
It can be as simple as this:
DECLARE #Name VARCHAR(500) = 'Roger';
SELECT #Name AS Name, UPPER(LEFT(#Name, 1)) + SUBSTRING(#Name, 2, LEN(#Name)) AS CapitalizedName;
fname is column name if fname value is akhil then UPPER(left(fname,1)) provide capital First letter(A) and substring function SUBSTRING(fname,2,LEN(fname)) provide(khil) concate both using + then result is (Akhil)
select UPPER(left(fname,1))+SUBSTRING(fname,2,LEN(fname)) as fname
FROM [dbo].[akhil]
On SQL Server 2016+ using JSON which gives guaranteed order of the words:
CREATE FUNCTION [dbo].[InitCap](#Text NVARCHAR(MAX))
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN STUFF((
SELECT ' ' + UPPER(LEFT(s.value,1)) + LOWER(SUBSTRING(s.value,2,LEN(s.value)))
FROM OPENJSON('["' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(#Text,'\','\\'),'"','\"'),CHAR(9),'\t'),CHAR(10),'\n'),' ','","') + '"]') s
ORDER BY s.[key]
FOR XML PATH(''),TYPE).value('(./text())[1]','NVARCHAR(MAX)'),1,1,'');
END
GO
CREATE FUNCTION [dbo].[Capitalize](#text NVARCHAR(MAX)) RETURNS NVARCHAR(MAX) AS
BEGIN
DECLARE #result NVARCHAR(MAX) = '';
DECLARE #c NVARCHAR(1);
DECLARE #i INT = 1;
DECLARE #isPrevSpace BIT = 1;
WHILE #i <= LEN(#text)
BEGIN
SET #c = SUBSTRING(#text, #i, 1);
SET #result += IIF(#isPrevSpace = 1, UPPER(#c), LOWER(#c));
SET #isPrevSpace = IIF(#c LIKE '[ -]', 1, 0);
SET #i += 1;
END
RETURN #result;
END
GO
DECLARE #sentence NVARCHAR(100) = N'i-thINK-this soLUTION-works-LiKe-a charm';
PRINT dbo.Capitalize(#sentence);
-- I-Think-This Solution-Works-Like-A Charm
Here is the simplest one-liner to do this:
SELECT LEFT(column, 1)+ lower(RIGHT(column, len(column)-1) ) FROM [tablename]
I was looking for the best way to capitalize and i recreate simple sql script
how to use SELECT dbo.Capitalyze('this is a test with multiple spaces')
result "This Is A Test With Multiple Spaces"
CREATE FUNCTION Capitalyze(#input varchar(100) )
returns varchar(100)
as
begin
declare #index int=0
declare #char as varchar(1)=' '
declare #prevCharIsSpace as bit=1
declare #Result as varchar(100)=''
set #input=UPPER(LEFT(#input,1))+LOWER(SUBSTRING(#input, 2, LEN(#input)))
set #index=PATINDEX('% _%',#input)
if #index=0
set #index=len(#input)
set #Result=substring(#input,0,#index+1)
WHILE (#index < len(#input))
BEGIN
SET #index = #index + 1
SET #char=substring(#input,#index,1)
if (#prevCharIsSpace=1)
begin
set #char=UPPER(#char)
if (#char=' ')
set #char=''
end
if (#char=' ')
set #prevCharIsSpace=1
else
set #prevCharIsSpace=0
set #Result=#Result+#char
--print #Result
END
--print #Result
return #Result
end
IF OBJECT_ID ('dbo.fnCapitalizeFirstLetterAndChangeDelimiter') IS NOT NULL
DROP FUNCTION dbo.fnCapitalizeFirstLetterAndChangeDelimiter
GO
CREATE FUNCTION [dbo].[fnCapitalizeFirstLetterAndChangeDelimiter] (#string NVARCHAR(MAX), #delimiter NCHAR(1), #new_delimeter NCHAR(1))
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #result NVARCHAR(MAX)
SELECT #result = '';
IF (LEN(#string) > 0)
DECLARE #curr INT
DECLARE #next INT
BEGIN
SELECT #curr = 1
SELECT #next = CHARINDEX(#delimiter, #string)
WHILE (LEN(#string) > 0)
BEGIN
SELECT #result =
#result +
CASE WHEN LEN(#result) > 0 THEN #new_delimeter ELSE '' END +
UPPER(SUBSTRING(#string, #curr, 1)) +
CASE
WHEN #next <> 0
THEN LOWER(SUBSTRING(#string, #curr+1, #next-2))
ELSE LOWER(SUBSTRING(#string, #curr+1, LEN(#string)-#curr))
END
IF (#next > 0)
BEGIN
SELECT #string = SUBSTRING(#string, #next+1, LEN(#string)-#next)
SELECT #next = CHARINDEX(#delimiter, #string)
END
ELSE
SELECT #string = ''
END
END
RETURN #result
END
GO