Replace every 2nd instance of character in string with TSQL - sql-server

I have a field that contains a string of lat/long co-ordinates that define a geofence (polygon). Each is seperated by a comma.
eg: 'lat,long,lat,long,lat,long'
eg: 148.341158,-21.500773,148.341406,-21.504989,148.375136,-21.513174,148.401674,-21.535247,148.418044,-21.532767,148.408867,-21.511685,148.414075,-21.508461,148.36968,-21.432567,148.349094,-21.438768,148.346862,-21.480187,148.341158,-21.500773,
I'd like to use this with the geography type in MSSQL (http://msdn.microsoft.com/en-us/library/bb933971.aspx)
DECLARE #g geography;
SET #g = geography::STPolyFromText('POLYGON((-122.358 47.653, -122.348 47.649, -122.348 47.658, -122.358 47.658, -122.358 47.653))', 4326);
SELECT #g.ToString();
This seems to require: 'lat long, lat long, lat long' ie: no comma between the pair.
I can't change the source data, as it's used by a vendor program. I need to manipulate the string to remove every 1 out of 2 commas, or failing that, get regex working in TSQL

You could use a numbers table (a very handy tool for many purposes, by the way) to find the positions of all the commas, then, using only the odd position numbers, replace the corresponding commas with spaces in a single SELECT statement. Here's what I am talking about:
WITH CTE AS (
SELECT number, rn = ROW_NUMBER() OVER (ORDER BY number)
FROM master.dbo.spt_values
WHERE type = 'P'
AND number BETWEEN 1 AND LEN(#coords)
AND SUBSTRING(#coords, number, 1) = ','
)
SELECT
#coords = STUFF(#coords, number, 1, ' ')
FROM CTE
WHERE rn % 2 = 1
;
In the above query, the numbers table's "part" is "played" by a subset of system table master.dbo.spt_values. The CTE calculates the positions of all the commas in the #coords string, returning the results as a row set. The main SELECT is used an assignment statement. It takes every number in the CTE set and removes the character at the corresponding position in #coords, replacing it with a space character (all with the help of the STUFF function).
You can use this SQL Fiddle demo to play with the query.

This ended up my Solution:
DECLARE #WorkingCoordList VARCHAR(max) = 'some lat,long,lat,long string'
DECLARE #Processed INT = 0
DECLARE #CommaLoc INT
DECLARE #Count INT = 0
WHILE #Processed = 0
BEGIN
SET #CommaLoc = PATINDEX('%,%', #WorkingCoordList)
IF #Count % 2 = 0
BEGIN
SET #WorkingCoordList = STUFF(#WorkingCoordList, #CommaLoc, 1, ' ') --Convert comma to space
END
ELSE
BEGIN
SET #WorkingCoordList = STUFF(#WorkingCoordList, #CommaLoc, 1, '#') -- Convert comma to hash
END
IF #CommaLoc = LEN(#WorkingCoordList)
BEGIN
SET #WorkingCoordList = LEFT(#WorkingCoordList, LEN(#WorkingCoordList) - 1) -- trim trailing ,
SET #WorkingCoordList = RTRIM(LTRIM(REPLACE(#WorkingCoordList, '#', ', '))) -- Convert all the hashes to commas
SET #Processed = 1
END
SET #Count = #Count + 1
END
END

You could roll your own parser like the following. It uses the commas in the string to find all of the lattitude/ longitude values. It concatenates all the values together using the pattern: lat long, lat long, ...
declare #list varchar(max)
declare #result varchar(max)
declare #word varchar(max)
declare #splitOn varchar(1)
declare #wpos int
declare #cpos int
declare #wordCount int
select #list = '148.341158,-21.500773,148.341406,-21.504989,148.375136,-21.513174,148.401674,-21.535247,148.418044,-21.532767,148.408867,-21.511685,148.414075,-21.508461,148.36968,-21.432567,148.349094,-21.438768,148.346862,-21.480187,148.341158,-21.500773,'
select #splitOn = ','
select #result = ''
select #cpos = 0
select #wpos = 1
select #wordCount = 1
while (#cpos <= len(#list))
begin
select #cpos = charindex(#splitOn, #List, #cpos)
if (#cpos < 1) select #cpos = len(#list) + 1
select #word = substring(#list, #wpos, #cpos - #wpos)
select #result = #result + ' ' + #word
if ((#wordCount % 2) = 0 and (#cpos < len(#list))) select #result = #result + ','
select #cpos = #cpos + 1
select #wpos = #cpos
select #wordCount = #wordCount + 1
end
select #result as result
Which produces the following string:
148.341158 -21.500773, 148.341406 -21.504989, 148.375136 -21.513174, 148.401674 -21.535247, 148.418044 -21.532767, 148.408867 -21.511685, 148.414075 -21.508461, 148.36968 -21.432567, 148.349094 -21.438768, 148.346862 -21.480187, 148.341158 -21.500773

I don't know how your regex works but if you preprocess the string with a regex, it might work with a global search and replace like this:
find: ,([^,]*(?:,|$))
replace: '$1' ie. space plus capture group 1

Thanks for the code :-)
I had a slightly different scenario however created a function using "chue x" code to add a character '#' at every 3rd ';'
/*** My select query ***/
SELECT dbo.fn_AddEveryNthItem(a.MyString, ';','#', 3) AS Expr1
FROM dbo.MyTable as a
Function is below
/*** Function to add character for every nth item ***/
Create function dbo.fn_AddEveryNthItem(#list varchar(1000), #splitOn varchar(1), #addChar varchar(1), #EveryNthItem int)
RETURNS VARCHAR(1000)
AS
BEGIN
declare #word varchar(max)
declare #result varchar(max) = ''
declare #wpos int = 1
declare #cpos int = 0
declare #wordCount int =1
while (#cpos <= len(#list))
begin
select #cpos = charindex(#splitOn, #List, #cpos)
if (#cpos < 1) select #cpos = len(#list) + 1
select #word = substring(#list, #wpos, #cpos - #wpos)
select #result = #result + #splitOn + #word
if ((#wordCount % #EveryNthItem) = 0 and (#cpos < len(#list))) select #result = #result + #addChar
select #cpos = #cpos + 1
select #wpos = #cpos
select #wordCount = #wordCount + 1
end
Return #result
end

Related

How to get split string with quotation on each split item in SQL Server?

How to get split string with quotation on each split item in SQL Server? I have tried this
declare #departmentNames as varchar(max) = 'Account, hod'
--declare #departmentNames as varchar(max) = 'Account'+', '+'hod'
print #departmentNames
I get result like this => Account,hod
but I want it like this => 'Account', 'hod'
so that I could use it in
select *
from tblDepartment
where name in (select item from splitString(#departmentNames, ','))
I know if I use integers with id column it will work fine i.e => 1, 2, 3, 4 but I want to try it with strings.
So is there anyone who can help me with this?
You can use apply :
select td.*
from tblDepartment td cross apply
<shema>.splitString(#departmentNames, ',') spt(item) -- add schema name
where spt.item = td.name;
If you want string comparison, you can do concatenation.
Note : use Schema name while calling UDF function.
First create this function:
CREATE FUNCTION [dbo].[fn_Split]
(#String varchar(8000),
#Delimiter varchar(50))
RETURNS #temptable TABLE (items varchar(8000))
AS
BEGIN
/*
SELECT * FROM dbo.fn_Split('12345;thome', ';')
*/
DECLARE #idx int
DECLARE #slice varchar(8000)
DECLARE #delimiterLength int
SET #delimiterLength = len(#Delimiter)
SELECT #idx = 1
IF LEN(#String) < 1 OR #String IS NULL
RETURN
WHILE #idx != 0
BEGIN
SET #idx = CHARINDEX(#Delimiter, #String)
IF #idx != 0
SET #slice = LEFT(#String, #idx - 1)
ELSE
SET #slice = #String
IF (LEN(#slice) > 0)
INSERT INTO #temptable(Items)
VALUES (LTRIM(RTRIM(#slice)))
SET #String = RIGHT(#String, LEN(#String) - #idx - #delimiterLength + 1)
IF LEN (#String) = 0
BREAK
END
RETURN
END
After creating this function then you can test with below query.
It splits words with any delimiter you are passing
select items from dbo.fn_Split('ACCOUNT ,HOD',',')
select items from dbo.fn_Split('ACCOUNT ; HOD',';')
Then pass variable and and use join with this function.
Use table alias for easy understanding
declare #departmentNames as varchar(max) = ('Account, hod')
select t.*
from tblDepartment t
inner join
(Select items
from dbo.fn_Split (#departmentNames, ',')) A on t.name = A.items
I create temptable for testing and this query will return output like below

How do I replace non word characters with a dash using TSQL?

How do I replace the characters ~!##$%^&*()_+}{][ in a nvarchar (or varchar) field with a - using TSQL?
you can create user define function for that as given below
CREATE FUNCTION udf_ReplaceSpecialChar
(
#inputString VARCHAR(1000)
)
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE #outputString VARCHAR(1000),
#LENGTH INT,
#index INT,
#char CHAR(1)
SELECT #LENGTH = LEN(#inputString),
#index = 1
WHILE(#index <= #LENGTH)
BEGIN
SET #char = SUBSTRING(#inputString, #index, 1)
IF((ASCII(#char) NOT BETWEEN 65 AND 90) AND (ASCII(#char) NOT BETWEEN 97 AND 122) AND (ASCII(#char) NOT BETWEEN 48 AND 57))
BEGIN
SELECT #inputString = REPLACE(#inputString, #char, '-')
END
SET #index = #index + 1
END
SET #outputString = #inputString
RETURN #outputString
END
SELECT dbo.udf_ReplaceSpecialChar('This()*& is%%#Sample**.>String')
or you should replace each character with '-'
Like
SELECT REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE('This()*& is%%#Sample**.>String', ' ', '-'), '*', '-'), '#', '-'), '&', '-'), '(', '-'), ')', '-'), '.', '-'), '>', '-'), '%', '-')
You can use REPLACE function. If it doesn't work in some cases, please give us examples.
May be this code is that you are searching for:
-- Author: Christian d'Heureuse, www.source-code.biz
create function dbo.RemoveSpecialChars (#s varchar(256)) returns varchar(256)
with schemabinding
begin
if #s is null
return null
declare #s2 varchar(256)
set #s2 = ''
declare #l int
set #l = len(#s)
declare #p int
set #p = 1
while #p <= #l begin
declare #c int
set #c = ascii(substring(#s, #p, 1))
if #c between 48 and 57 or #c between 65 and 90 or #c between 97 and 122
set #s2 = #s2 + char(#c)
set #p = #p + 1
end
if len(#s2) = 0
return null
return #s2
end
It removes all characters except 0-9, a-z and A-Z. This function uses ASCII codes of characters to determine this ones which must be removed.
--another one variant
----------------------------------------------------------------------------------------
--better to keep such table in server, very usefull table, especially with indexes
DECLARE #Tally TABLE ( N INT )
DECLARE #i AS INT = 1
WHILE #i != 1000
BEGIN
INSERT INTO #Tally
( N )
VALUES ( #i )
SET #i = #i + 1
END
----------------------------------------------------------------------------------------
DECLARE #String AS VARCHAR(1000) = 'This()*& is%%# **.>another one //&^&*$variant'
----------------------------------------------------------------------------------------
--using #tally - split, using like - remove not required, 'for xml ...' - combine into string
SELECT REPLACE(( SELECT LEFT(SUBSTRING(#String, n, 1000), 1)
FROM #Tally AS T
WHERE SUBSTRING(#String, n, 1000) != ''
AND LEFT(SUBSTRING(#String, n, 1000), 1) LIKE '[A-Za-z0-9 ]'
FOR
XML PATH('')
), ' ', ' ')
--another one variant
------------------------------------------------------------------------------------
--better to keep such table in server, very usefull table, especially with indexes
DECLARE #Tally TABLE ( N INT )
DECLARE #i AS INT = 1
WHILE #i != 1000
BEGIN
INSERT INTO #Tally
( N )
VALUES ( #i )
SET #i = #i + 1
END
------------------------------------------------------------------------------------
DECLARE #String VARCHAR(500) ,
#B VARCHAR(500) = ''
SET #String = 'This()*& is%%# **.>another one //&^&*$variant'
SELECT #B = #B + SUBSTRING(#String, t.N, 1)
FROM #Tally t
WHERE t.N <= DATALENGTH(#String)
AND PATINDEX('[A-Za-z0-9 ]', SUBSTRING(#String, t.N, 1)) > 0
SELECT #B
--------------------------------------------------------------------------------
if you wish use this method like a function then:
Create Tally table with one field PRIMARY KEY (1000 rows, starting from 1 with step 1)
Use code below to create function
Table Tally will be very useful for the split sting, clean string etc., currently this is
the best way to use instead fetch, xml and etc.
--------------------------------------------------------------------------------
CREATE FUNCTION [dbo].[StringClean](
#A VARCHAR(500))
RETURNS VARCHAR(500)
AS
BEGIN
DECLARE #B VARCHAR(500)
SET #B = ''
SELECT #B = #B + SUBSTRING(#A, t.N, 1)
FROM dbo.Tally t
WHERE t.N <= DATALENGTH(#A)
AND PATINDEX('[A-Za-z0-9 ]', SUBSTRING(#A, t.N, 1)) > 0
RETURN #B
END
-------------------------------------------------------------------------------
SELECT dbo.StringClean('This()*& is%%# **.>another one //&^&*$variant')
-------------------------------------------------------------------------------
DECLARE #Tally TABLE ( N INT )
DECLARE #i AS INT = 1
WHILE #i != 1000
BEGIN
INSERT INTO #Tally (N) VALUES (#i)
SET #i = #i + 1
END
--------------------------------------------------------------
DECLARE #String VARCHAR(500)
DECLARE #B VARCHAR(500) = ''
DECLARE #ReplacedChars VARCHAR(50) = '~!##$%^&*()_+}{][<>/.'
SET #String = 'This()*& is%%# **.>another one //&^&*$variant'
SELECT #B = #B + CASE WHEN CHARINDEX(SUBSTRING(#String, t.N, 1), #ReplacedChars) > 0 THEN '-'
ELSE SUBSTRING(#String, t.N, 1) END
FROM #Tally t
WHERE t.N <= DATALENGTH(#String)
SELECT #B

Removing special characters and numbers in sql table field

I have a table in sql server 2008 where a column contains field with string and special characters like
abc;#34;pqr. Now I want a function to remove special characters and numbers from that field ;so the output would be like abcpqr.
Try this
DECLARE #str VARCHAR(400)
DECLARE #expres VARCHAR(50) = '%[~,#,#,$,%,&,*,(,),.,!,0-9]%'
SET #str = '(remove) ~special~ 10 *characters. 3 5 from string 1 in sql!'
WHILE PATINDEX( #expres, #str ) > 0
SET #str = Replace(REPLACE( #str, SUBSTRING( #str, PATINDEX( #expres, #str ), 1 ),''),'-',' ')
SELECT #str
REFERENCE
Use The following function
/*********************************
Removes any characters from
#myString that do not meet the
provided criteria.
*********************************/
CREATE FUNCTION dbo.GetCharacters(#myString varchar(500), #validChars varchar(100))
RETURNS varchar(500) AS
BEGIN
While #myString like '%[^' + #validChars + ']%'
Select #myString = replace(#myString,substring(#myString,patindex('%[^' + #validChars + ']%',#myString),1),'')
Return #myString
END
Go
Declare #testStr varchar(1000),
#i int
Set #i = 1
while #i < 255
Select
#TestStr = isnull(#TestStr,'') + isnull(char(#i),''),
#i = #i + 1
Select #TestStr
Select dbo.GetCharacters(#TestStr,'a-z')
Select dbo.GetCharacters(#TestStr,'0-9')
Select dbo.GetCharacters(#TestStr,'0-9a-z')
Select dbo.GetCharacters(#TestStr,'02468bferlki')
Try this :-
DECLARE #var varchar(255) = 'abc;#34;pqr';
SELECT
CONVERT(varchar(255),(
SELECT CASE
WHEN ( ASCII(UPPER(SUBSTRING(#var, Number, 1))) BETWEEN 65 and 90 )
THEN SUBSTRING(#var, Number, 1)
END
FROM
(
Select top(255) number FROM [master]..spt_values
where type = 'p'
) AS n
WHERE Number <= LEN(#var)
FOR XML PATH(''))) as Result
Result
abcpqr

Replace unicode number between to different delimiters with NCHAR function

I've created a MSSQL Server function which encodes special chars (example: हिन्दीabcde fG#) to the unicode number with "#" and ";" as delimiter. Only very simple chars like "abc" will not be encoded:
declare #position int, #txt nvarchar(max), #output as varchar(max);
set #position=1;
set #txt = N'हिन्दीabcde fG#';
set #output = '';
while #position <= len(#txt)
begin
declare #t int;
select #t=unicode(substring(#txt,#position,1))
--print '&#'+ CONVERT(nvarchar(5),#t)+';'
if ( (#t between 48 and 57) OR (#t between 65 and 90) or (#t between 97 and 122) )
BEGIN
SET #output = #output + CONVERT(nvarchar(5), substring(#txt,#position,1) );
END
else
BEGIN
SET #output = #output + '#'+ CONVERT(nvarchar(5),#t)+';'
END
set #position = #position+1
end
Print #output
The result is:
2361;#2367;#2344;#2381;#2342;#2368;abcde#32;fG#35;
I need it for working with ODBC drivers and to avoid problems with special chars.
But now I need the way back - to decode the encoded chars. Is there any smart solution or will I need at least two loops, the "NCHAR" function ...?
I'll try to build such a function - if it's successfull, I'll post it here :)
You might find this approach a little more appealing. First, create a split function that maintains order:
CREATE FUNCTION dbo.SplitStringsOrdered
(
#List NVARCHAR(MAX),
#delim NVARCHAR(10)
)
RETURNS TABLE
AS
RETURN
(
SELECT rn, v = LTRIM(RTRIM(SUBSTRING(#List, rn,
CHARINDEX(#delim, #List + #delim, rn) - rn)))
FROM
(
SELECT TOP (8000) rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
) AS n
WHERE rn <= LEN(#List)
AND SUBSTRING(#delim + #List, rn, LEN(#delim)) = #delim
);
GO
Usage:
DECLARE #x NVARCHAR(MAX) = N'#2361;#2367;#2344;#2381;'
+ N'#2342;#2368;abcde#32;fG#35;';
-- need one small adjustment to make the string more split-friendly:
SET #x = REPLACE(#x, '#', ';#');
DECLARE #output NVARCHAR(MAX);
SELECT #output = (SELECT
CASE WHEN v LIKE '#%' THEN NCHAR(REPLACE(v, '#', '')) ELSE v END
FROM dbo.SplitStringsOrdered(#x, ';') AS x
ORDER BY rn FOR XML PATH(''),
TYPE).value('./text()[1]','nvarchar(max)');
SELECT #output;
Output:
हिन्दीabcde fG#
I've solved the problem with this query:
declare #position int, #txt nvarchar(max), #output as nvarchar(max), #buffer as varchar(max);
set #position=1;
set #txt = '#2361;#2367;#2344;#2381;#2342;#2368;abcde#32;fG#35;';
set #output = '';
set #buffer = '';
while #position <= len(#txt)
begin
declare #t varchar(max);
select #t=(substring(#txt,#position,1))
if ( len(#buffer) = 0 and #t <> '#' and #t <> ';')
BEGIN
-- Append simple chars, which were not encoded
Print 'Hänge den String ganz normal an den Output: ' + #t
SET #output = #output + #t;
END
ELSE
BEGIN
if ( #t = '#' )
BEGIN
Print 'Raute wurde erkannt: #';
SET #buffer = '#';
END
else if ( #t = ';' )
BEGIN
SET #buffer = REPLACE( #buffer, '#' , '' );
Print 'Umwandeln: ' + #buffer
SET #output = #output + isnull( NCHAR(#buffer) , '');
SET #buffer = '';
END
else
BEGIN
Print 'Ganzzahl an den Buffer anhängen: ' + #t;
SET #buffer = #buffer + #t;
END
END
set #position = #position+1
end
Print #output

How do you prepend space in a string where Upper Case letter comes or where a space really needed [duplicate]

This question already has answers here:
Finding Uppercase Character then Adding Space
(3 answers)
Closed 8 years ago.
How do you prepends space in a string where Upper Case letter comes or where a space really needed.
The Sample code is:
DECLARE #teams TABLE (Team NVARCHAR(100))
INSERT INTO #teams
SELECT 'TataConsultencyServices'
UNION ALL
SELECT 'TataConsultencyCompany'
UNION ALL
SELECT 'CompanyHumanResource'
Expected Result
Tata Consultency Services
Tata Consultency Company
Company Human Resource
A set based solution:
DECLARE #s NVARCHAR(100);
SET #s = 'CompanyHumanResources';
DECLARE #Idx INT = 1;
WITH CteRecursive
AS
(
SELECT 1 AS Idx,
CONVERT(NVARCHAR(200), #s) AS String
UNION ALL
SELECT src.Idx + src.IsUpper + 1,
CONVERT(NVARCHAR(200),
CASE WHEN src.IsUpper = 1 THEN STUFF(src.String, src.Idx+1, 0, ' ') ELSE src.String END
)
FROM
(
SELECT rec.*,
CASE WHEN SUBSTRING(rec.String, rec.Idx, 1) <> ' ' AND SUBSTRING(rec.String, rec.Idx+1, 1) LIKE '[A-Z]' AND SUBSTRING(rec.String, rec.Idx+1, 1) COLLATE Romanian_CS_AS = UPPER(SUBSTRING(rec.String, rec.Idx+1, 1)) COLLATE Romanian_CS_AS THEN 1 ELSE 0 END AS IsUpper
FROM CteRecursive rec
WHERE rec.Idx + 1 <= LEN(rec.String)
) src
)
SELECT TOP(1) x.String
FROM CteRecursive x
ORDER BY x.Idx DESC;
Results:
String
-----------------------
Company Human Resources
You may surely get some help from this:-
CREATE FUNCTION CaseSensitiveSQLSplitFunction
(
#str nvarchar(max)
)
returns #t table (val nvarchar(max))
as
begin
declare #i int, #j int
select #i = 1, #j = len(#str)
declare #w nvarchar(max)
while #i <= #j
begin
if substring(#str,#i,1) = UPPER(substring(#str,#i,1)) collate Latin1_General_CS_AS
begin
if #w is not null
insert into #t (val) select #w
set #w = substring(#str,#i,1)
end
else
set #w = #w + substring(#str,#i,1)
set #i = #i + 1
end
if #w is not null
insert into #t (val) select #w
return
end
Taking the sample as:-
declare #str nvarchar(max) = N'ThisIsATest'
select * from dbo.CaseSensitiveSQLSplitFunction(#str)
set #str = N'ThisIsASqlServerCaseSensitiveSplitStringFunction'
select * from dbo.CaseSensitiveSQLSplitFunction(#str)
It is now possible to sql concatenate string values in a way from rows to single column value.
We can just use any of the sql concatenation function.
declare #str nvarchar(max) = N'ThisIsATest'
SELECT LTRIM(STUFF((
SELECT ' ' + val FROM dbo.CaseSensitiveSQLSplitFunction(#str) FOR XML PATH('')
), 1, 1, '')) string
set #str = N'ThisIsASqlServerCaseSensitiveSplitStringFunction'
SELECT LTRIM(STUFF((
SELECT ' ' + val FROM dbo.CaseSensitiveSQLSplitFunction(#str) FOR XML PATH('')
), 1, 1, '')) string
WHILE 1 = 1
BEGIN
UPDATE #teams
SET TeamName = STUFF(TeamName, patindex('%[a-z,.][A-Z]%', TeamName COLLATE Latin1_General_BIN) + 1,0,' ')
WHERE patindex('%[a-z,.][A-Z]%', TeamName COLLATE Latin1_General_BIN) > 0
IF ##ROWCOUNT = 0 BREAK
END
UPDATE #teams
SET TeamName = STUFF(TeamName, patindex('%[A-Z][a-z]%', RIGHT(TeamName,LEN(TeamName) -1) COLLATE Latin1_General_BIN) +1 ,0,' ')
WHERE patindex('%[A-Z][a-z]%', TeamName COLLATE Latin1_General_BIN) > 0

Resources