How to generate spaces between letters in Sql Server 2005 (Set Based) - sql-server-2005

Input
Column
ab2e
mnop
a2t1y
output
Id Col1 Col2 Col3 Col4 Col5 Col6
1 a b e
2 m n o p
3 a t y
The numbers indicates the number of spaces
Since in the first input, there is 2 after b, so the letter e will appear after 2 spaces from b.
In the second input since there is no space, the the letters will appear after each other
Thanks

If you've already got a way of distributing a 'normal' string's contents between the columns and only need a solution for expanding strings like ab2e into strings like ab[space][space]e, then here's a possible solution:
DECLARE #InputString varchar(100), #pos int, #result varchar(100);
SET #InputString = 'a2t1y';
SET #result = #InputString;
SET #pos = PATINDEX('%[0-9]%', #result);
WHILE #pos <> 0 BEGIN
SET #result = STUFF(#result, #pos, 1, SPACE(SUBSTRING(#result, #pos, 1)));
SET #pos = PATINDEX('%[0-9]%', #result);
END
SELECT #result;
The output:
---------------------
a t y
It would probably be a nice idea to implement it as a function:
CREATE FUNCTION ExpandString (#String varchar(100))
RETURNS varchar(100)
AS BEGIN
DECLARE #pos int, #result varchar(100);
SET #result = #String;
SET #pos = PATINDEX('%[0-9]%', #result);
WHILE #pos <> 0 BEGIN
SET #result = STUFF(#result, #pos, 1, SPACE(SUBSTRING(#result, #pos, 1)));
SET #pos = PATINDEX('%[0-9]%', #result);
END
RETURN #result;
END
so you could call it on a column like this:
SELECT …, dbo.ExpandString(t.SomeColumn), …
It should be noted, though, that this solution only supports single-digit 'macros', i.e. a12b would be converted to a[1 space][2 spaces]b with this function, which is not necessarily what you'd expect. So, if you need it to recognise integers as sequences of numeric characters between non-numerics, here's an alternative solution:
CREATE FUNCTION ExpandString (#String varchar(100))
RETURNS varchar(100)
AS BEGIN
DECLARE #pos int, #lastpos int, #len int, #isnum bit,
#sub varchar(100), #result varchar(100);
SET #result = '';
SET #pos = 1;
SET #len = LEN(#String);
SET #isnum = ISNUMERIC(SUBSTRING(#String, #pos, 1));
WHILE #pos <= #len BEGIN
SET #lastpos = #pos;
WHILE #pos <= #len AND ISNUMERIC(SUBSTRING(#String, #pos, 1)) = #isnum
SET #pos = #pos + 1;
SET #sub = SUBSTRING(#String, #lastpos, #pos - #lastpos);
SET #result = #result + CASE #isnum WHEN 1 THEN SPACE(#sub) ELSE #sub END;
SET #isnum = #isnum ^ 1;
END;
RETURN #result;
END
Both versions recognise numbers both at the beginning and at the end of the input string.

Related

Proper/Title Case a Column with Exceptions table in SQL Server

I am trying to convert a column which is in upper case to proper case but with exceptions like certain acronyms, abbreviations. I am following the below code to implement that. But looks like this will be an ongoing process and so, I want to create a table with the exceptions in order to make it easy to clean the data and I want to be able to call the exceptions table from the function. It would be great if anyone can help me with any codes they have which is similar to this or any ideas on how to implement it.
ALTER FUNCTION [dbo].[Business_ProperCase]
(#Text AS VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
BEGIN
-- declare some variables
DECLARE #Reset BIT; DECLARE #Ret VARCHAR(8000); DECLARE #i INT;
DECLARE #c0 CHAR(1); DECLARE #c1 CHAR(1); DECLARE #c2 CHAR(1);
DECLARE #CaseLen INT;
DECLARE #CaseExceptions VARCHAR(8000);
DECLARE #CaseValue VARCHAR(8000);
-- Set some default values
SELECT #Reset = 1, #i=1, #Ret = '';
-- only apply if all characters are already in uppercase
IF (UPPER(#Text)=#Text COLLATE Latin1_General_CS_AI)
BEGIN
-- add a leading and trailing space to indicate word delimiters (bol & eol)
SET #Text = ' ' + #Text + ' ';
-- cycle through each character,
-- if non-alpha, uppercase next alpha character.
-- if alpha then lowercase subsequent alphas.
WHILE (#i <= LEN(#Text))
SELECT
#c0=SUBSTRING(#Text,#i-2,1), #c1=SUBSTRING(#Text,#i-1,1), #c2=SUBSTRING(#Text,#i,1),
#Ret = #Ret + CASE WHEN #Reset=1 THEN UPPER(#c2) ELSE LOWER(#c2) END,
#Reset = CASE
WHEN #c0 = ' ' AND #c1 = 'M' AND #c2 = 'c' THEN 1
WHEN #c0 = ' ' AND #c1 IN ('D', 'I', 'O') AND #c2 = '''' THEN 1
WHEN #c2 LIKE '[a-zA-Z'']' THEN 0 -- Apply LOWER to any character after alphas or apostrophes
ELSE 1 -- Apply UPPER to any character after symbols/punctuation
END,
#i = #i +1
-- add a trailing space in case the previous rule changed this.
SET #Ret = #Ret + ' ';
-- custom exceptions: this search is case-insensitive and will
-- replace the word to the case as it is written in the list.
-- NOTE: this list has to end with a comma!
SELECT #i=0, #CaseLen=0,
#CaseExceptions = 'ABS,LLC,MD,MBA,MA,
--Want to create a table for these exceptions and call them from this function
-- Loop through exception cases
WHILE CHARINDEX(',', #CaseExceptions, #i+1)>0
BEGIN
-- get the delimited word
SET #CaseLen = CHARINDEX(',', #CaseExceptions, #i+1) - #i
SET #CaseValue = SUBSTRING(#CaseExceptions, #i, #CaseLen)
-- replace it in the original text
SET #Ret = REPLACE(#Ret, ' '+#CaseValue+' ', ' '+#CaseValue+' ')
-- get position of next word
SET #i = CHARINDEX(',', #CaseExceptions, #i+#CaseLen) +1
END
-- remove any leading and trailing spaces
SET #Ret = LTRIM(RTRIM(#Ret));
-- capitalize first character of data irrespective of previous rules
SET #Ret = UPPER(SUBSTRING(#Ret,1,1)) + SUBSTRING(#Ret,2,LEN(#Ret));
END
ELSE
BEGIN
-- return the string unaffected if it is not in uppercase
SET #Ret=#Text
END
RETURN #Ret
END
Create a table (I use TITLE_CASE_EXCEPTION as my example) with a column EXCEPTION
Then it is data driven from there.
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[GUI].[fn_TITLE_CASE]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
DROP FUNCTION [GUI].[fn_TITLE_CASE]
GO
CREATE FUNCTION [GUI].[fn_TITLE_CASE]
(
#STRING VARCHAR(MAX)
)
RETURNS VARCHAR(MAX)
AS
BEGIN
SET QUOTED_IDENTIFIER OFF
DECLARE #RESET BIT
DECLARE #_OUT_STRING VARCHAR(MAX)
DECLARE #I INT
DECLARE #C CHAR(1)
DECLARE #CASE_LEN INT = 0
DECLARE #CASE_EXCEPTIONS VARCHAR(MAX) = ''
DECLARE #CASE_VALUE VARCHAR(MAX) = ''
IF #STRING IS NULL
RETURN NULL
IF #STRING = ''
RETURN #STRING
SELECT #STRING = LOWER(RTRIM(#STRING)), #RESET = 1, #I = 1, #_OUT_STRING = ''
WHILE (#I <= LEN(#STRING))
SELECT
#C = SUBSTRING(#STRING, #I, 1),
#_OUT_STRING = #_OUT_STRING + CASE WHEN #RESET = 1 THEN UPPER(#C) ELSE #C END,
#RESET = CASE WHEN #C LIKE '[a-zA-Z'']' THEN 0 ELSE 1 END,
#I = #I + 1
SELECT #I = 0, #_OUT_STRING = #_OUT_STRING + ' '
SELECT #CASE_EXCEPTIONS = #CASE_EXCEPTIONS + RTRIM(EXCEPTION) + ',' FROM [LOOKUP].TITLE_CASE_EXCEPTION
WHILE CHARINDEX(',', #CASE_EXCEPTIONS, #I + 1) > 0
BEGIN
-- get the delimited word
SET #CASE_LEN = CHARINDEX(',', #CASE_EXCEPTIONS, #I + 1) - #I
SET #CASE_VALUE = SUBSTRING(#CASE_EXCEPTIONS, #I, #CASE_LEN)
-- replace it in the original text
SET #_OUT_STRING = REPLACE(#_OUT_STRING, ' ' + #CASE_VALUE + ' ', ' ' + #CASE_VALUE + ' ')
-- get position of next word
SET #I = CHARINDEX(',', #CASE_EXCEPTIONS, #I + #CASE_LEN) + 1
END
RETURN RTRIM(#_OUT_STRING)
END
GO
Here's an example for you to reference:
declare #s varchar(256) = 'This is a SQL test';
declare #t table (ignore varchar(256) not null);
insert into #t (ignore) values ('SQL');
declare #pos int = 1;
declare #nextpos int;
declare #w varchar(256);
while #pos <= len(#s)
begin
set #nextpos = charindex(' ', #s + ' ', #pos);
set #w = substring(#s, #pos, #nextpos - #pos);
if not exists (select 1 from #t where ignore = #w)
set #s = stuff(
#s, #pos, #nextpos - #pos,
stuff(lower(#w), 1, 1, upper(left(#w, 1)))
);
set #pos = #nextpos + 1;
select #s;
end
To answer the original request.. set up a table "Exceptions" with a single column ConcatList of type nvarchar (100) and add the exceptions to this table... then create a view with to concatenate them together...
create table exceptions (ConcatList nvarchar(100))
create view [dbo].vExceptions
as
Select distinct
substring(
(
Select ','+ up.ConcatList AS [text()]
From exceptions up
ORDER BY up.ConcatList
For XML PATH ('')
), 2, 4000) [exceptions]
From exceptions p
Here is a slightly enhanced version of the stored procedure from the question.
(although an admittedly inelegant solution) to account for:
Lower case words (of, the, an, etc)
Hhyphenated acronyms
Exceptions that are immediately preceeded or followed with a dash or comma.
alter FUNCTION [dbo].[Business_ProperCase]
(#Text AS VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
BEGIN
-- declare some variables
DECLARE #Reset BIT; DECLARE #Ret VARCHAR(8000); DECLARE #i INT;
DECLARE #c0 CHAR(1); DECLARE #c1 CHAR(1); DECLARE #c2 CHAR(1);
DECLARE #CaseLen INT;
DECLARE #CaseExceptions VARCHAR(8000);
DECLARE #CaseValue VARCHAR(8000);
-- Set some default values
SELECT #Reset = 1, #i=1, #Ret = '';
-- only apply if all characters are already in uppercase
IF (UPPER(#Text)=#Text COLLATE Latin1_General_CS_AI)
BEGIN
-- add a leading and trailing space to indicate word delimiters (bol & eol)
SET #Text = ' ' + #Text + ' ';
-- cycle through each character,
-- if non-alpha, uppercase next alpha character.
-- if alpha then lowercase subsequent alphas.
WHILE (#i <= LEN(#Text))
SELECT
#c0=SUBSTRING(#Text,#i-2,1), #c1=SUBSTRING(#Text,#i-1,1), #c2=SUBSTRING(#Text,#i,1),
#Ret = #Ret + CASE WHEN #Reset=1 THEN UPPER(#c2) ELSE LOWER(#c2) END,
#Reset = CASE WHEN #c0 = ' ' AND #c1 = 'M' AND #c2 = 'c' THEN 1
WHEN #c0 = ' ' AND #c1 IN ('D', 'I', 'O') AND #c2 = '''' THEN 1
WHEN #c2 LIKE '[a-zA-Z'']' THEN 0 -- Apply LOWER to any character after alphas or apostrophes
ELSE 1 -- Apply UPPER to any character after symbols/punctuation
END,
#i = #i +1
-- add a trailing space in case the previous rule changed this.
SET #Ret = #Ret + ' ';
-- custom exceptions: this search is case-insensitive and will
-- replace the word to the case as it is written in the list.
-- NOTE: this list has to end with a comma!
SELECT #i=0, #CaseLen=0,
#CaseExceptions = exceptions from vExceptions
--Want to create a table for these exceptions and call them from this function
-- Loop through exception cases
WHILE CHARINDEX(',', #CaseExceptions, #i+1)>0
BEGIN
-- get the delimited word
SET #CaseLen = CHARINDEX(',', #CaseExceptions, #i+1) - #i
SET #CaseValue = SUBSTRING(#CaseExceptions, #i, #CaseLen)
if (#CaseValue = 'OF' or #CaseValue = 'AND' or #CaseValue ='THE' or #CaseValue='FOR')
begin
--replace with lower case 'of', 'and', 'the', 'for'
SET #Ret = REPLACE(#Ret, ' '+#CaseValue+' ', ' '+lower(#CaseValue)+' ')
end
else
begin
if (CHARINDEX(' '+ #CaseValue +' ', #Ret)>0 )
begin
-- replace it in the original text
SET #Ret = REPLACE(#Ret, ' '+#CaseValue+' ', ' '+#CaseValue+' ')
end
else if (CHARINDEX(' '+#CaseValue+',', #Ret)>0 )
begin
--replace text (with no spaces around it)
SET #Ret = REPLACE(#Ret, ' '+#CaseValue+',', ' '+#CaseValue+',')
end
else if (CHARINDEX(' '+#CaseValue+'-', #Ret)>0 )
begin
--replace text (with no spaces around it)
SET #Ret = REPLACE(#Ret, ' '+#CaseValue+'-', ' '+#CaseValue+'-')
end
else if (CHARINDEX('-'+#CaseValue+' ', #Ret)>0 )
begin
--replace text (with no spaces around it)
SET #Ret = REPLACE(#Ret, '-'+#CaseValue+' ', '-'+#CaseValue+' ')
end
else if (CHARINDEX(','+#CaseValue+' ', #Ret)>0 )
begin
--replace text (with no spaces around it)
SET #Ret = REPLACE(#Ret, ','+#CaseValue+' ', '-'+#CaseValue+' ')
end
end
-- get position of next word
SET #i = CHARINDEX(',', #CaseExceptions, #i+#CaseLen) +1
END
-- remove any leading and trailing spaces
SET #Ret = LTRIM(RTRIM(#Ret));
-- capitalize first character of data irrespective of previous rules
SET #Ret = UPPER(SUBSTRING(#Ret,1,1)) + SUBSTRING(#Ret,2,LEN(#Ret));
END
ELSE
BEGIN
-- return the string unaffected if it is not in uppercase
SET #Ret=#Text
END
RETURN #Ret
END
Create a table (I use ExceptionsTable as my example) with a column WordExcepts. Then add the following after your last DECLARE at the top of the page:
DECLARE #sql nvarchar(2000);
SET #sql = 'N select WordExcepts from ExceptionsTable'
Then down below adjust your exceptions to be:
#CaseExceptions = #sql
Just add to your table as needed and they get filtered out of the function.

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

How to split Url in Sql server

Here is the url
www.abc.com/a/b/c/d/e/f/g/h/i
I want result like this
www.abc.com/a/b/c/d/e/f/g/h/i
www.abc.com/a/b/c/d/e/f/g/h
www.abc.com/a/b/c/d/e/f/g
www.abc.com/a/b/c/d/e/f
www.abc.com/a/b/c/d/e
www.abc.com/a/b/c/d
www.abc.com/a/b/c
www.abc.com/a/b
www.abc.com/a
www.abc.com/
Use While loop. Try this.
DECLARE #result TABLE(string VARCHAR(500))
DECLARE #str VARCHAR(500)='www.abc.com/a/b/c/d/e/f/g/h/i',
#cntr INT=1,
#len INT
SET #len = Len(#str)
WHILE #cntr <= #len
BEGIN
IF Charindex('/', #str) > 0
BEGIN
SELECT #str = LEFT(#str, Len(#str) - 2)
INSERT INTO #result
SELECT #str
END
ELSE
BREAK
SET #cntr+=1
END
SELECT * FROM #result
I got another solution for the same.
declare #S nvarchar(100) = 'www.abc.com/a/b/c/d/e/f/g/h/i'
while PATINDEX('%[/]%' , #S) > 0 BEGIN
SET #S = LEFT (#S,LEN(#S) - PATINDEX('%[/]%' , REVERSE(#S)))
SELECT #S
END

take minus(-) sign number from sting and use in in different procedure

I have variable
DECLARE #Routs NVARCHAR(1024)
#i int
SET #Routs = N'6,4,-5,8'
I need to extract any number from this sting, where it have minus sign before it (-5 in example)
and use it as input parameter with out (-) sing for example #i in different stored procedure.
Pass in you're #Routs parameter to a table valued function that will split the list into a table and then loop through the table and if the value is a negative number execute stored procedure or whatever you want or do nothing if its not negative.
--table function to split parameter by comma
ALTER FUNCTION [dbo].[SplitListOfInts] (#list nvarchar(MAX))
RETURNS #tbl TABLE (number int NOT NULL) AS
BEGIN
DECLARE #pos int,
#nextpos int,
#valuelen int
if len(rtrim(#list)) > 0
begin
SELECT #pos = 0, #nextpos = 1
WHILE #nextpos > 0
BEGIN
SELECT #nextpos = charindex(',', #list, #pos + 1)
SELECT #valuelen = CASE WHEN #nextpos > 0
THEN #nextpos
ELSE len(#list) + 1
END - #pos - 1
INSERT #tbl (number)
VALUES (convert(int, substring(#list, #pos + 1, #valuelen)))
SELECT #pos = #nextpos
END
end
RETURN
END
-- stored procedure that calls that split function and uses #routs parameter
CREATE TABLE #values(nbrValue int)
INSERT INTO #values(nbrValue
EXEC [dbo].[SplitListOfInts] #routs
--if you don't care about non-negatives delete them here
DELETE FROM #values
where nbrValue >= 0
DECLARE #i int
DECLARE #countrows = (SELECT COUNT(nbrValue) FROM #values)
WHILE #countrows >0
SET #i = (SELECT TOP 1 nbrValue FROM #values)
...do what you want
DELETE FROM #values where nbrValue=#i
set #countrows = (SELECT COUNT(nbrValue) FROM #values)
END

I have IndustryID like '2,4,5' ...and wants to display results assiciate with those IndustryID. SQL SERVER

I have IndustryID like '2,4,5' ... and wants to display results associate with those IndustryID. But the thing is in details table it stores value as 2,4,5 that is comma separated. So using IN operator is only getting IndustryID equal to 2 or 4 or 5.
This is my query
Declare #IndustryID varchar(50)
set #IndustryID='2,4,5'
select IndustryID,Industry from DetailsMaster
where (Industry IN (SELECT * from fnList2Table (#IndustryID))))
fnList2Table is this function here:
ALTER FUNCTION [dbo].[fnList2Table]
(
#List varchar(MAX)
)
RETURNS
#ParsedList table
(
item varchar(MAX)
)
AS
BEGIN
DECLARE #item varchar(800), #Pos int
SET #List = LTRIM(RTRIM(#List))+ ','
SET #Pos = CHARINDEX(',', #List, 1)
WHILE #Pos > 0
BEGIN
SET #item = LTRIM(RTRIM(LEFT(#List, #Pos - 1)))
IF #item <> ''
BEGIN
INSERT INTO #ParsedList (item)
VALUES (CAST(#item AS int))
END
SET #List = RIGHT(#List, LEN(#List) - #Pos)
SET #Pos = CHARINDEX(',', #List, 1)
END
RETURN
END
Please provide answer.
Use this UDF which converts comma seperated string into row values and just use that in query like --- IN (select dbo.udf_splitStringToRows(#IndustryID))
CREATE FUNCTION dbo.udf_List2Table
(
#List VARCHAR(MAX),
#Delim CHAR
)
RETURNS #ParsedList TABLE
(
item VARCHAR(MAX)
)
AS
BEGIN
DECLARE #item VARCHAR(MAX), #Pos INT
SET #List = LTRIM(RTRIM(#List))+ #Delim
SET #Pos = CHARINDEX(#Delim, #List, 1)
WHILE #Pos > 0 BEGIN
SET #item = LTRIM(RTRIM(LEFT(#List, #Pos - 1)))
IF #item <> '' BEGIN
INSERT INTO #ParsedList (item)
VALUES (CAST(#item AS VARCHAR(MAX)))
END
SET #List = RIGHT(#List, LEN(#List) - #Pos)
SET #Pos = CHARINDEX(#Delim, #List, 1)
END
RETURN
END
GO