SQL - split a string using "/" that occurs multiple times - sql

I'm looking for a query for HSQLDB.
I have a string that contains address information delimited by "/". Now I have to split this string by "/" and insert individual address information into separate columns.
Address = /1234/CLAREVIEW////WILMINGTON/DE/19702
This needs to be split as
StreetNo = Address[1] = 1234
StreetName = Address[2] = CLAREVIEW
StreetType = Address[3] =
City = Address[6] = WILMINGTON
StateCd = Address[7] = DE
ZipCd = Address[8] = 19702
How can i achieve this?

CREATE PROCEDURE with the REGEXP_SUBSTRING_ARRAY function to split into an array.
REGEXP_SUBSTRING_ARRAY('/1234/CLAREVIEW////WILMINGTON/DE/19702', '/\p{Alnum}*');
Returns
ARRAY['/1234','/CLAREVIEW','/','/','/','/WILMINGTON','/DE','/19702']
So the procedure should contain:
CREATE PROCEDURE INSERT_USING_REGEXP (p1 VARCHAR(500))
BEGIN ATOMIC
DECLARE arr VARCHAR(200) ARRAY;
SET arr = REGEXP_SUBSTRING_ARRAY(p1,'/\p{Alnum}*');
INSERT INTO thetable ((StreetNo, StreetName, StreetType...) VALUES ( arr[1], arr[2], arr[3], ...);
END;
Then
CALL INSERT_USING_REGEXP('/1234/CLAREVIEW////WILMINGTON/DE/19702');

CREATE TABLE #Results
(
Ordinal NUMERIC,
StringValue VARCHAR(MAX)
)
DECLARE #String VARCHAR(100),
#Delimiter VARCHAR(100)
SET #String = '/1234/CLAREVIEW////WILMINGTON/DE/19702'
SET #Delimiter = '/'
DECLARE #TempString VARCHAR(MAX) = #String,
#Ordinal INT = 0,
#CharIndex INT = 0
SET #CharIndex = CHARINDEX(#Delimiter, #TempString)
WHILE #CharIndex != 0
BEGIN
SET #Ordinal += 1
INSERT #Results
VALUES (#Ordinal, SUBSTRING(#TempString, 0, #CharIndex))
SET #TempString = SUBSTRING(#TempString, #CharIndex + 1, LEN(#TempString) - #CharIndex)
SET #CharIndex = CHARINDEX(#Delimiter, #TempString)
END
IF #TempString != ''
BEGIN
SET #Ordinal += 1
INSERT #Results
VALUES (#Ordinal, #TempString)
END
SELECT *
FROM #Results
I took this answer from here but it should do the trick

Related

Generate Next Alphanumeric Code on SQL Server

I need to create a consecutive sequence of varchar(5) (always 5 chars only) code starting from PREVIOUS code.
For example
'00000', '00001', '00002'...'00009', '0000A', '0000B'...'0000Z', '00010','00011'...'ZZZZZ'.
So if I have #PREVIOUS_CODE = '00000', #NEXT_CODE will be '00001'.
If I have #PREVIOUS_CODE = '00009', #NEXT_CODE will be '0000A'
If I have #PREVIOUS_CODE = '0000Z', #NEXT_CODE will be '00010'
So I need something like that
USE [DATABASE]
GO
CREATE PROCEDURE [dbo].[spGetNextCode]
#PREVIOUS_CODE VARCHAR(5)
AS
DECLARE #NEXT_CODE VARCHAR(5)
DO STUFF
...
SELECT #NEXT_CODE AS NEXT_CODE
GO
Any Help?
Just keep an integer counter in the same table and convert it. I'm using the following SQL Server function in one of my applications:
CREATE FUNCTION [dbo].[GetAlphanumericCode]
(
#number BIGINT,
#leadingzeroes INT = 0
)
RETURNS varchar(255)
AS
BEGIN
DECLARE #charPool varchar(36)
SET #charPool = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
DECLARE #result varchar(255)
IF #number < 0
RETURN ''
IF #number = 0
SET #result = '0'
ELSE BEGIN
SET #result = ''
WHILE (#number > 0)
BEGIN
SET #result = substring(#charPool, #number % 36 + 1, 1) + #result
SET #number = #number / 36
END
END
IF #leadingzeroes > 0 AND len(#result) < #leadingzeroes
SET #result = right(replicate('0', #leadingzeroes) + #result, #leadingzeroes)
RETURN #result
END
It should be a trivial task to rewrite it as a stored procedure

String Parsing in MSSQL

I'm looking at pulling two numeric values from a single text string in SQL (2012 version i believe).
The strings are in the following possible formats:
A1234 B4567
or
A:1234 B:4567
or random variants of
A[Symbol/Space/Nothing][Numberstring1] [Space/Nothing] B[Symbol/Space/Nothing][Numberstring2]
Ideally I'd use a combo of substring and charindex but because the symbols/space/nothing at all are used at random i'm finding it difficult.
My preferred output would be 2 columns, one with the [NumberString1] one with [Numberstring2]
Any ideas folks?
You could give this approach a try, it will give you a list of all the number groups. You could then select from the resulting table.
CREATE FUNCTION [dbo].[ParseOutNumbers](#inputText varchar(1000))
RETURNS #ParsedValues TABLE (ID int IDENTITY(1,1),numVal varchar(1000))
AS
BEGIN
DECLARE #charIndex INT
DECLARE #number varchar(1000)
DECLARE #nextChar varchar(1000)
SELECT #charIndex = 1
SELECT #number = ''
WHILE #charIndex <= LEN(#inputText)
BEGIN
SELECT #nextChar = SUBSTRING(#inputText, #charIndex, 1);
IF ISNUMERIC(#nextChar) = 1
BEGIN
SELECT #number = #number + #nextChar
END
IF (ISNUMERIC(#nextChar) = 0 OR #charIndex = LEN(#inputText))
BEGIN
IF (LEN(#number) > 0)
BEGIN
INSERT #ParsedValues(numVal) VALUES(#number)
SELECT #number = ''
END
END
SELECT #charIndex = #charIndex + 1
END
RETURN
END
GO
select * from [dbo].[ParseOutNumbers]('A/d11222Bdd:22002 C23002')
select * from [dbo].[ParseOutNumbers]('A:11222 B:22002 C:23002')
select * from [dbo].[ParseOutNumbers]('A112442B22502C3002')
Alternate version:
CREATE FUNCTION [dbo].[ParseOutNumbersRev2](#inputText varchar(1000))
RETURNS #ParsedValues TABLE (A varchar(1000), B varchar(1000))
AS
BEGIN
DECLARE #charIndex INT
DECLARE #number varchar(1000)
DECLARE #nextChar varchar(1000)
DECLARE #valueIndex INT
SELECT #charIndex = 1
SELECT #number = ''
SELECT #valueIndex = 0
WHILE #charIndex <= LEN(#inputText)
BEGIN
SELECT #nextChar = SUBSTRING(#inputText, #charIndex, 1);
IF ISNUMERIC(#nextChar) = 1
BEGIN
SELECT #number = #number + #nextChar
END
IF (ISNUMERIC(#nextChar) = 0 OR #charIndex = LEN(#inputText))
BEGIN
IF (LEN(#number) > 0)
BEGIN
IF (#valueIndex = 0)
BEGIN
INSERT #ParsedValues(A, B) VALUES(#number, null)
END
ELSE IF (#valueIndex = 1)
BEGIN
UPDATE #ParsedValues SET B = #number
END
SELECT #number = ''
SELECT #valueIndex = #valueIndex + 1
END
END
SELECT #charIndex = #charIndex + 1
END
RETURN
END
GO
select * from [dbo].[ParseOutNumbersRev2]('A/d11222Bdd:22002 C23002')
select * from [dbo].[ParseOutNumbersRev2]('A:11222 B:56 C:23002')
select * from [dbo].[ParseOutNumbersRev2]('A112442B22502C3002')

Dynamically update column value with replacement for some pattern

I have my table with Column MailText which has values like
1. <strong>abc</strong>:<description1> <strong>bcd</strong>:<description2>
2. <strong>efg</strong>:<description3> <strong>hgl</strong>:<description7>
Upon update I want values like
1. <strong>abc</strong>:<abc> <strong>bcd</strong>:<bcd>
2. <strong>efg</strong>:<efg> <strong>hgl</strong>:<hgl>
Please help with dynamic replacement that it would update all string within <strong> tag to <description>. <strong> tag may contain any values.
CREATE FUNCTION GetString
(
#s NVARCHAR(MAX)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #trav NVARCHAR(2000) = #s,
#length INT,
#count INT = 1,
#startIndex INT = 0,
#endIndex INT = 0,
#replaceStartIndex INT = 0,
#repalceEndIndex INT = 0,
#replaceword NVARCHAR(2000),
#newWord NVARCHAR(2000)
SELECT #length = LEN(#Trav)
WHILE ((#count + #startIndex) <= #length)
BEGIN
SET #startIndex = CHARINDEX('<strong>', #trav, #startIndex) + LEN('<strong>')
IF (#startIndex > 8)
BEGIN
SET #endIndex = CHARINDEX('</strong>', #trav, #startIndex)
SET #newWord = SUBSTRING(#trav, #startIndex, (#endIndex - #startIndex))
SET #replaceStartIndex = CHARINDEX(':', #trav, #startIndex) + 2
SET #repalceEndIndex = CHARINDEX('>', #trav, #replaceStartIndex)
SET #replaceword = SUBSTRING(
#trav,
#replaceStartIndex,
(#repalceEndIndex - #replaceStartIndex)
)
--SELECT #replaceword as 'repword', #newWord as 'newword'
SET #trav = REPLACE (#trav, #replaceword, #newWord)
SET #count = #repalceEndIndex
END
ELSE
BEGIN
SET #count = #count + 1
END
END
RETURN #trav
END
GO
IF OBJECT_ID('tempdb..#table') IS NOT NULL
DROP TABLE #table
CREATE TABLE #table
(
string VARCHAR(1000)
)
INSERT INTO #table
SELECT
'1. <strong>abc</strong>:<description1> <strong>bcd</strong>:<description2>'
INSERT INTO #table
SELECT
'2. <strong>efg</strong>:<description3> <strong>hgl</strong>:<description7>'
UPDATE #table
SET string = [dbo].[GetString](#table.string)
SELECT *
FROM #table

Converting String List into Int List in SQL

I have a nvarchar(MAX) in my stored procedure which contains the list of int values, I did it like this as it is not possible to pass int list to my stored procedure,
but, now I am getting problem as my datatype is int and I want to compare the list of string.
Is there a way around by which I can do the same?
---myquerry----where status in (#statuslist)
but the statuslist contains now string values not int, so how to convert them into INT?
UPDate:
USE [Database]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SP]
(
#FromDate datetime = 0,
#ToDate datetime = 0,
#ID int=0,
#List nvarchar(MAX) //This is the List which has string ids//
)
AS
SET FMTONLY OFF;
DECLARE #sql nvarchar(MAX),
#paramlist nvarchar(MAX)
SET #sql = 'SELECT ------ and Code in(#xList)
and -------------'
SELECT #paramlist = '#xFromDate datetime,#xToDate datetime,#xId int,#xList nvarchar(MAX)'
EXEC sp_executesql #sql, #paramlist,
#xFromDate = #FromDate ,#xToDate=#ToDate,#xId=#ID,#xList=#List
PRINT #sql
So when I implement that function that splits then I am not able to specify the charcter or delimiter as it is not accepting it as (#List,',').
or (','+#List+',').
It is possible to send an int list to your stored procedure using XML parameters. This way you don't have to tackle this problem anymore and it is a better and more clean solution.
have a look at this question:
Passing an array of parameters to a stored procedure
or check this code project:
http://www.codeproject.com/Articles/20847/Passing-Arrays-in-SQL-Parameters-using-XML-Data-Ty
However if you insist on doing it your way you could use this function:
CREATE FUNCTION [dbo].[fnStringList2Table]
(
#List varchar(MAX)
)
RETURNS
#ParsedList table
(
item int
)
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
Call it like this:
SELECT *
FROM Table
WHERE status IN (SELECT * from fnStringList2Table(#statuslist))
You can work with string list too. I always do.
declare #statuslist nvarchar(max)
set #statuslist = '1, 2, 3, 4'
declare #sql nvarchar(max)
set #sql = 'select * from table where Status in (' + #statuslist + ')'
Execute(#sql)
You can do this by using sql function which will return you an integer array..
It would be great if you pass #Delimiter separated string to your stored procedure which could be processed properly afterwards.
Write one function to split the data as following
CREATE FUNCTION [dbo].[SplitValues] (#StringArray NVARCHAR(MAX), #Delimiter NVARCHAR(10))
RETURNS #ResultedValues table
(
ResultValue INT
)
AS
BEGIN
DECLARE #Tokens TABLE(Token nvarchar)
DECLARE #String nvarchar
WHILE (CHARINDEX(#Delimiter,#StringArray)>0)
BEGIN
INSERT INTO #Tokens (Token) VALUES (LTRIM(RTRIM(SUBSTRING(#StringArray,1,CHARINDEX(#Delimiter,#StringArray)-1))))
SET #String = SUBSTRING(#StringArray,
CHARINDEX(#Delimiter,#StringArray)+LEN(#Delimiter),LEN(#StringArray))
END
INSERT INTO #ResultedValues (ResultValue ) VALUES ( CAST(LTRIM(RTRIM(#String)) AS INT))
RETURN
END
And then use it like following, i am using (,) as #Delimiter here
SELECT ResultValue [YourSchema].[SplitValues](#statuslist,',')
Actually, you can send the list of int values to your procedure by creating a User Defined Table Type. However, this implies more work in order to populate the table parameter.
In your case, you can use the sp_executesql stored procedure to achieve what you want like this:
declare #statement nvarchar(4000) = '----your query---- where status in ('
+ #statusList +')'
sp_executesql #statement
here is an example of how to do it and the Link for more informations
ALTER FUNCTION iter_intlist_to_tbl (#list nvarchar(MAX))
RETURNS #tbl TABLE (listpos int IDENTITY(1, 1) NOT NULL,
number int NOT NULL) AS
BEGIN
DECLARE #startpos int,
#endpos int,
#textpos int,
#chunklen smallint,
#str nvarchar(4000),
#tmpstr nvarchar(4000),
#leftover nvarchar(4000)
SET #textpos = 1
SET #leftover = ''
WHILE #textpos <= datalength(#list) / 2
BEGIN
SET #chunklen = 4000 - datalength(#leftover) / 2
SET #tmpstr = ltrim(#leftover + substring(#list, #textpos, #chunklen))
SET #textpos = #textpos + #chunklen
SET #startpos = 0
SET #endpos = charindex(' ' COLLATE Slovenian_BIN2, #tmpstr)
WHILE #endpos > 0
BEGIN
SET #str = substring(#tmpstr, #startpos + 1, #endpos - #startpos - 1)
IF #str <> ''
INSERT #tbl (number) VALUES(convert(int, #str))
SET #startpos = #endpos
SET #endpos = charindex(' ' COLLATE Slovenian_BIN2, #tmpstr, #startpos + 1)
END
SET #leftover = right(#tmpstr, datalength(#tmpstr) / 2 - #startpos)
END
IF ltrim(rtrim(#leftover)) <> ''
INSERT #tbl (number) VALUES(convert(int, #leftover))
RETURN
END
-- ############################ Example ############################
--CREATE PROCEDURE get_product_names_iter #ids varchar(50) AS
--SELECT P.ProductName, P.ProductID
--FROM Northwind..Products P
--JOIN iter_intlist_to_tbl(#ids) i ON P.ProductID = i.number
--go
--EXEC get_product_names_iter '9 12 27 37'
-- ############################ WICHTIG ############################
This works for me on an Informix DataBase:
DROP FUNCTION rrhh:fnc_StringList_To_Table;
CREATE FUNCTION rrhh:fnc_StringList_To_Table (pStringList varchar(250))
RETURNING INT as NUMERO;
/* A esta Funcion le podes pasar una cadena CSV con una lista de numeros
* Ejem: EXECUTE FUNCTION fnc_StringList_To_Table('1,2,3,4');
* y te devolvera una Tabla con dichos numeros separados uno x fila
* Autor: Jhollman Chacon #Cutcsa - 2019 */
DEFINE _STRING VARCHAR(255);
DEFINE _LEN INT;
DEFINE _POS INT;
DEFINE _START INT;
DEFINE _CHAR VARCHAR(1);
DEFINE _VAL INT;
LET _STRING = REPLACE(pStringList, ' ', '');
LET _START = 0;
LET _POS = 0;
LET _LEN = LENGTH(_STRING);
FOR _POS = _START TO _LEN
LET _CHAR = SUBSTRING(pStringList FROM _POS FOR 1);
IF _CHAR <> ',' THEN
LET _VAL = _CHAR::INT;
ELSE
LET _VAL = NULL;
END IF;
IF _VAL IS NOT NULL THEN
RETURN _VAL WITH RESUME;
END IF;
END FOR;
END FUNCTION;
EXECUTE FUNCTION fnc_StringList_To_Table('1,2,3,4');
SELECT * FROM TABLE (fnc_StringList_To_Table('1,2,3,4'));

SQL Split function that handles string with delimeter appearing between text qualifiers?

There are several SQL split functions, from loop driven, to using xml commands, and even using a numbers table. I haven't found one that supports text qualifiers.
Using the example string below, I would like to split on ",", but not when it appears between double or single quotes.
Example data:
jsmith#anywhere.com, "Sally \"Heat\" Jones" <sally#anywhere.com>, "Mark Jones" <mjones#anywhere.com>, "Stone, Ron" <rstone#anywhere.com>
Should return a table:
jsmith#anywhere.com
"Sally \"Heat\" Jones" <sally#anywhere.com>
"Mark Jones" <mjones#anywhere.com>
"Stone, Ron" <rstone#anywhere.com>
I know this is a complex query/function, but any suggestions or any guidance would be mucho appreciated.
Here is my solution:
CREATE FUNCTION fnSplitString
(
#input nvarchar(MAX)
)
RETURNS #emails TABLE
(
email nvarchar(MAX)
)
AS
BEGIN
DECLARE #len int = LEN(#input)
DECLARE #pos int = 1;
DECLARE #start int = 1;
DECLARE #ignore bit = 0;
WHILE(#pos<=#len)
BEGIN
DECLARE #ch nchar(1) = SUBSTRING(#input, #pos, 1);
IF ( #ch = '"' or #ch = '''')
BEGIN
SET #ignore = 1 - #ignore;
END
IF (#ch = ',' AND #ignore = 0)
BEGIN
INSERT #emails VALUES (SUBSTRING(#input, #start, #pos-#start));
SET #start = #pos+1;
END
SET #pos = #pos + 1;
END
IF (#start<>#pos)
BEGIN
INSERT #emails VALUES (SUBSTRING(#input, #start, #pos-#start));
END
RETURN
END
GO
DECLARE #input nvarchar(max) = 'jsmith#anywhere.com, "Sally \"Heat\" Jones" <sally#anywhere.com>, "Mark Jones" <mjones#anywhere.com>, "Stone, Ron" <rstone#anywhere.com>';
select * from fnSplitString(#input)
CREATE FUNCTION [dbo].[udfSplit]
(
#nvcString nvarchar(max),
#nvcDelimiter nvarchar(1),
#nvcTQ nvarchar(1)
)
RETURNS #tblTokens TABLE (
Token nvarchar(max)
)
AS
BEGIN
DECLARE #intCounter int
DECLARE #nvcToken nvarchar(4000)
DECLARE #nvcCurrentChar nvarchar(1)
DECLARE #intStart int
IF #nvcString <> ''
BEGIN
SET #intCounter = 1
SET #nvcToken = ''
SET #intStart = 0
--Loop through each character of the string
WHILE #intCounter <= LEN(#nvcString)
BEGIN
SET #nvcCurrentChar = SUBSTRING(#nvcString, #intCounter, 1)
--If current char is TQ
IF #nvcCurrentChar = #nvcTQ
BEGIN
--Concatonate to token
SET #nvcToken = #nvcToken + #nvcCurrentChar
--If this is the end TQ
IF #intStart <> 0
BEGIN
--Fix TQ
SET #nvcToken = dbo.udfRemoveTQFromToken(#nvcToken, #nvcTQ)
IF #nvcToken <> ''
BEGIN
INSERT INTO #tblTokens (Token) VALUES (#nvcToken)
SET #nvcToken = ''
END
--Reset TQ
SET #intStart = 0
END
ELSE
BEGIN
SET #nvcToken = dbo.udfRemoveTQFromToken(#nvcToken, #nvcTQ)
IF #nvcToken <> ''
BEGIN
INSERT INTO #tblTokens (Token) VALUES (#nvcToken)
SET #nvcToken = ''
END
--Mark TQ start position
SET #intStart = #intCounter
END
END
ELSE IF #intStart = 0 AND #nvcCurrentChar = #nvcDelimiter
BEGIN
--If not inside TQ, and char is Delimiter
SET #nvcToken = dbo.udfRemoveTQFromToken(#nvcToken, #nvcTQ)
IF #nvcToken <> ''
BEGIN
INSERT INTO #tblTokens (Token) VALUES (#nvcToken)
SET #nvcToken = ''
END
END
ELSE
BEGIN
--Current char is not TQ or Delim, add to current token
SET #nvcToken = #nvcToken + #nvcCurrentChar
END
SET #intCounter = #intCounter + 1
END
END
SET #nvcToken = dbo.udfRemoveTQFromToken(#nvcToken, #nvcTQ)
IF #nvcToken <> ''
BEGIN
--Current Token has not been added to table
INSERT INTO #tblTokens (Token) VALUES (#nvcToken)
END
RETURN
END
GO
CREATE FUNCTION [dbo].[udfRemoveTQFromToken]
(
#nvcToken nvarchar(4000),
#nvcTQ nvarchar(1)
)
RETURNS nvarchar(4000) AS
BEGIN
DECLARE #nvcReturn nvarchar(4000)
--Trim token, needs to be done first,
--as we dont want to trim any spaces within the TQ
--unless it was malformed
SET #nvcReturn = LTRIM(RTRIM(#nvcToken))
--If Left char is TQ
IF LEFT(#nvcReturn, 1) = #nvcTQ
BEGIN
--Though both cases perform the removal of the left most char (opening TQ)
--We need to perform a trim after removal ONLY if it was malformed
IF RIGHT(#nvcReturn, 1) <> #nvcTQ
BEGIN
--But no matching end TQ, malformed
--fix by removing left most char (the opening TQ)
SET #nvcReturn = RIGHT(#nvcReturn, LEN(#nvcReturn) - 1)
--Reapply the LTRIM, incase there were spaces after the opening TQ
SET #nvcReturn = LTRIM(#nvcReturn)
END
ELSE
BEGIN
--has matching end TQ, well-formed
--fix by removing left most char (the opening TQ)
SET #nvcReturn = RIGHT(#nvcReturn, LEN(#nvcReturn) - 1)
END
END
--Remove the right most char (the closing TQ)
IF RIGHT(#nvcReturn, 1) = #nvcTQ
SET #nvcReturn = LEFT(#nvcReturn, LEN(#nvcReturn) - 1)
RETURN #nvcReturn
END
This is a quick solution, and it is less than perfect, it has no stack, so it will treat the comma inside the quotes as the delimiter.
alter function fnSplit
(
#Delim char(1),
#List nvarchar(4000)
)
returns table as
return
with
Strings(PosIdx) as
(
select 1
union all
select PosIdx + 1 from Strings where PosIdx < 4000
)
select
ltrim(rtrim(substring(#List, PosIdx, charindex(#Delim, #List + #Delim, PosIdx) - PosIdx))) as value
from
Strings
where
PosIdx <= convert(int, len(#List))
and substring(#Delim + #List, PosIdx, 1) = #Delim
go
select * from fnSplit(',', 'jsmith#anywhere.com, "Sally \"Heat\" Jones" <sally#anywhere.com>, "Mark Jones" <mjones#anywhere.com>, "Stone, Ron" <rstone#anywhere.com>')
option (maxrecursion 0)