Split numbers from string - sql

I have my table like this
4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione
10-Contrassegno
12-Documenti di annullo polizza-10-Contrassegno
10-Contrassegno
12-Documenti di annullo polizza-10-Contrassegno
I want to split every row to be like this
4-3-2
10
12-10
10
12-10

try this
DECLARE #table AS TABLE
(
ID INT IDENTITY(1, 1) ,
SomeText VARCHAR(500)
)
INSERT INTO #table
( SomeText )
VALUES ( '4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione' ),
( '10-Contrassegno' ),
( '12-Documenti di annullo polizza-10-Contrassegno' ),
( '10-Contrassegno' ),
( '12-Documenti di annullo polizza-10-Contrassegno' );
WITH cte
AS ( SELECT n = 1
UNION ALL
SELECT n + 1
FROM cte
WHERE n <= 100
),
SplitToChr
AS ( SELECT T.ID ,
SUBSTRING(T.SomeText, cte.n, 1) AS Chr
FROM #table AS T
JOIN cte ON DATALENGTH(T.SomeText) >= cte.n
AND SUBSTRING(T.SomeText, cte.n, 1) LIKE '[0-9-]'
)
SELECT T.ID ,
REVERSE(SUBSTRING(REVERSE(T.FInal),2,LEN(T.FInal))) AS Final
FROM ( SELECT DISTINCT
o.ID ,
REPLACE(( SELECT '' + chr
FROM SplitToChr AS i
WHERE i.id = o.id
FOR
XML PATH('')
), '--', '-') AS FInal
FROM SplitToChr AS o
) AS T

SAMPLE TABLE
CREATE TABLE #TEMP(STRINGCOLUMN NVARCHAR(MAX))
INSERT INTO #TEMP
SELECT '4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione'
UNION ALL
SELECT '10-Contrassegno'
UNION ALL
SELECT '12-Documenti di annullo polizza-10-Contrassegno'
UNION ALL
SELECT '10-Contrassegno'
UNION ALL
SELECT '12-Documenti di annullo polizza-10-Contrassegno'
You can use a function in case you need the query to be simple
Function
CREATE FUNCTION [dbo].[ConvertToNumber]
(#strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256)
AS
BEGIN
SET #strAlphaNumeric = LEFT(#strAlphaNumeric,
LEN(#strAlphaNumeric) - CHARINDEX('-', REVERSE(#strAlphaNumeric)) + 0)
DECLARE #intAlpha INT
SET #intAlpha = PATINDEX('%[^0-9]-%', #strAlphaNumeric)
BEGIN
WHILE #intAlpha > 0
BEGIN
SET #strAlphaNumeric = STUFF(#strAlphaNumeric, #intAlpha, 1, '' )
SET #intAlpha = PATINDEX('%[^0-9]-%', #strAlphaNumeric )
END
END
RETURN ISNULL(#strAlphaNumeric,0)
END
And your final query
SELECT DBO.ConvertToNumber(STRINGCOLUMN)STRINGCOLUMN
FROM #TEMP
RESULT

first use this split func
CREATE FUNCTION [dbo].[fnSplitString] ( #string NVARCHAR(MAX)
, #delimiter CHAR(1) )
RETURNS #output TABLE ( splitdata NVARCHAR(MAX) )
BEGIN
DECLARE #start INT
, #end INT
SELECT #start = 1
, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1
BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output ( splitdata )
VALUES ( SUBSTRING(#string, #start, #end - #start) )
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
and then use this code
CREATE TABLE #t10 ( id int identity(1,1)
, num int )
INSERT INTO #t10
select *
from dbo.fnSplitString ('4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione','-')
WHERE ISNUMERIC(splitdata)=1
DECLARE #x int = 1
DECLARE #y varchar(10)=''
DECLARE #q varchar(10)
WHILE #x<=(SELECT count(*)
from #t10)
BEGIN
SET #q=(SELECT num
FROM #t10
WHERE id = #x )
set #y=#y+#q+'-'
SET #x=#x+1
END
SELECT left(ltrim(#y),len(#y)-1)
--SELECT * FROM #t10
--drop table #t10

It's a bit messy, but it gets the job done :-)
CREATE TABLE dbo.Documents (
Details VARCHAR(1000)
);
GO
INSERT INTO dbo.Documents
VALUES ('4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione'),
('10-Contrassegno'),
('12-Documenti di annullo polizza-10-Contrassegno'),
('10-Contrassegno'),
('12-Documenti di annullo polizza-10-Contrassegno');
SELECT CASE WHEN (FirstLine + SecondLine + ThirdLine) LIKE '%-'
THEN LEFT(FirstLine + SecondLine + ThirdLine, LEN(FirstLine + SecondLine + ThirdLine) -1)
ELSE (FirstLine + SecondLine + ThirdLine) END AS CleanData
FROM (
SELECT
LEFT(Details, CHARINDEX('-', Details)) AS FirstLine,
REPLACE(CASE
WHEN REPLACE( REPLACE( LEFT(Details,CHARINDEX('-', Details, CHARINDEX('-', Details )+1)+2) , LEFT(Details, CHARINDEX('-', Details, CHARINDEX('-', Details)+1)), ''), LEFT(Details, CHARINDEX('-', Details)), '') LIKE '%[0-9]' THEN REPLACE( REPLACE( LEFT(Details,CHARINDEX('-', Details, CHARINDEX('-', Details )+1)+2) , LEFT(Details, CHARINDEX('-', Details, CHARINDEX('-', Details)+1)), ''), LEFT(Details, CHARINDEX('-', Details)), '') + '-'
ELSE REPLACE( LEFT(Details,CHARINDEX('-', Details, CHARINDEX('-', Details )+1)+2) , LEFT(Details, CHARINDEX('-', Details, CHARINDEX('-', Details)+1)), '')
END , LEFT(Details, CHARINDEX('-', Details)), '') AS SecondLine,
CASE WHEN REVERSE(LEFT(REVERSE(Details), CHARINDEX('-', REVERSE(Details))+2)) LIKE '-%'
THEN LEFT(REVERSE(LEFT(REVERSE(Details), CHARINDEX('-', REVERSE(Details))+2)), 3) ELSE '' END AS ThirdLine
FROM dbo.Documents
) AS A;

Another way of doing it in a much simpler way. Create a function to remove the alphabets from the string
Create FUNCTION dbo.RemoveAlphabets (#string VARCHAR(256))
returns VARCHAR(256)
BEGIN
IF #string IS NULL
RETURN NULL
DECLARE #Result VARCHAR(256)='',#len INT = Len(#string ),#cnt INT=1
WHILE #cnt <= #len
BEGIN
DECLARE #parse INT
SET #parse = Ascii(Substring(#string , #cnt, 1))
IF #parse BETWEEN 48 AND 57 or #parse =45
SET #Result = #Result + Char(#parse)
SET #cnt = #cnt + 1
END
select #result= replace(#Result,'--','-')
RETURN left(#result,case when right(#Result,1)='-' then len(#Result)-1 else len(#result) end)
END
If numbers inside the string is always surrounded by '-' then replace the return statement with this.
RETURN left(#result,len(#Result)-1)
execute the function
select dbo.RemoveAlphabets
('4-Documento d’identità-3-Attestato di Rischio-2-Carta di Circolazione')
Result : 4-3-2

Related

How to store semicolon separated long string into multiple columns?

Basically due to restriction on live server, I cannot use text file so I need to hard code the data in T-SQL code.
So first I created string from text file like this:
("001122;Sale Item 1", "001123;Sale Item 23", "001124;Sale Item 24", .... )
I have this table structure:
DECLARE #Product TABLE(ProductCode INT NOT NULL, Description nvarchar(100) NOT NULL)
First I need to store code and description in a table variable. Once that's done, then I can easily map it to the physical table and update the records.
How can I achieve this something similar to:
insert into #Product(ProductCode, Description)
values ("001122;Sale Item 1", "001123;Sale Item 23", "001124;Sale Item 24", .... )
Code Description
001122 Sale Item1
001123 Sale Item2
001124 Sale Item3
If you have fixed format like in example then you can achieve desired output simply using CHARINDEX and SUBSTRING
SELECT
SUBSTRING(description, 0, CHARINDEX(';', DESCRIPTION, 0)) code,
SUBSTRING(description, CHARINDEX(';', DESCRIPTION, 0)+1, LEN(DESCRIPTION)) Description
FROM #Product
OUTPUT:
code Description
------------------------
001122 Sale Item 1
001123 Sale Item 23
001124 Sale Item 24
I crated a sample for you, please check this
declare #Questions varchar(100)= '"001122;Sale Item 1", "001123;Sale Item 23", "001124;Sale Item 24"'
DECLARE #myXML AS XML = N'<H><r>' +Replace(#Questions, ',', '</r><r>') + '</r></H>'
select #myXML
;WITH cte
AS (
SELECT CAST(N'<H><r>' + Replace(Vals.id.value('.', 'NVARCHAR(50)') ,';' , '</r><r>') + '</r></H>' as XML) AS val
FROM #myXML.nodes('/H/r') AS Vals(id)
)
,mycte1 as (
SELECT distinct
Replace( S.a.value('(/H/r)[1]', 'NVARCHAR(50)') , '"', '') AS c1,
Replace( S.a.value('(/H/r)[2]', 'NVARCHAR(50)') , '"', '') AS c2
FROM cte CROSS APPLY val.nodes('/H/r') S(a)
)
select * from mycte1
The output will be
c1 c2
001123 Sale Item 23
001124 Sale Item 24
001122 Sale Item 1
Here we go matey:
DECLARE #MyString nvarchar(max) = '"001122;Sale Item 1", "001123;Sale Item 23", "001124;Sale Item 24"';
DECLARE #delimiter1 nvarchar(1)
, #delimiter2 nvarchar(1)
, #Start1 int
, #End1 int
, #Length int
, #cNEXTVALUE nvarchar(1000)
, #cNEXTVALUE2 nvarchar(1000)
, #StringLength int;
DECLARE #Product TABLE (
ProductCode int NOT NULL
, Description nvarchar(100) NOT NULL );
SET #MyString = RTRIM(LTRIM(#MyString));
SET #MyString = REPLACE(#MyString, CHAR(13), '');
SET #MyString = REPLACE(#MyString, CHAR(10), '');
SET #delimiter1 = ';';
SET #delimiter2 = ',';
SET #MyString = REPLACE(#MyString, '"', '')+#delimiter2;
SET #StringLength = LEN(#MyString);
SELECT #Start1 = 0
, #End1 = CHARINDEX(#delimiter1, #MyString, 1);
SELECT #Length = #End1 - #Start1;
WHILE #Length > 0
BEGIN
SET #cNEXTVALUE = SUBSTRING(#MyString, #Start1, #Length);
SET #Start1 = #End1 + 1;
SET #End1 = CHARINDEX(#delimiter2, #MyString, #Start1);
SELECT #Length = #End1 - #Start1;
SET #cNEXTVALUE2 = SUBSTRING(#MyString, #Start1, #Length);
IF LEN(RTRIM(LTRIM(#cNEXTVALUE))) > 0
BEGIN
INSERT INTO #Product
( ProductCode
, Description
)
VALUES
( #cNEXTVALUE, #cNEXTVALUE2 );
END;
SET #Start1 = #End1 + 2;
SET #End1 = CHARINDEX(#delimiter1, #MyString, #Start1);
SELECT #Length = #End1 - #Start1;
END;
SELECT *
FROM #Product;

MSSQL - Create table function, return substring

I need to create this table function. The function needs to return single words from passed parameters like: hello, hhuu, value
The table function should return:
hello,
hhuu,
value
But I am always getting some errors, please could you help me?
you can write as:
DECLARE #input_char VARCHAR(255)
SET #input_char = 'hello, hhuu, value'
;WITH cte AS (
SELECT
CAST('<r>' + REPLACE(#input_char, ' ', '</r><r>') + '</r>' AS XML) AS input_char
)
SELECT
rtrim( LTRIM (xTable.xColumn.value('.', 'VARCHAR(MAX)')) ) AS input_char
FROM cte
CROSS APPLY input_char.nodes('//r') AS xTable(xColumn)
Please give a look at this article:
http://www.codeproject.com/Tips/625872/Convert-a-CSV-delimited-string-to-table-column-in
and you could use ' ' (space) as delimiter.
SELECT * FROM dbo.CSVtoTable('hello, hhuu, value', ' ')
I have used the following function many times. It is a bit lengthy forgive me, but it has become a great tool for me.
CREATE Function [dbo].[ParseText2Table]
(
#p_SourceText varchar(MAX)
,#p_Delimeter varchar(100) = ',' --default to comma delimited.
)
RETURNS #retTable TABLE
(
POSITION INT
,Int_Value bigint
,Num_value REAL--Numeric(18,3)
,txt_value varchar(MAX)
)
AS
BEGIN
DECLARE #tmpTable TABLE
(
Position2 INT IDENTITY(1,1) PRIMARY KEY
,Int_Value bigint
,Num_value REAL--Numeric(18,3)
,txt_value varchar(MAX)
)
DECLARE #w_Continue INT
,#w_StartPos INT
,#w_Length INT
,#w_Delimeter_pos INT
,#w_tmp_int bigint
,#w_tmp_num REAL--numeric(18,3)
,#w_tmp_txt varchar(MAX)
,#w_Delimeter_Len INT
IF len(#p_SourceText) = 0
BEGIN
SET #w_Continue = 0 -- force early exit
END
ELSE
BEGIN
-- if delimiter is ' ' change
IF #p_Delimeter = ' '
BEGIN
SET #p_SourceText = replace(#p_SourceText,' ','ÿ')
SET #p_Delimeter = 'ÿ'
END
-- parse the original #p_SourceText array into a temp table
SET #w_Continue = 1
SET #w_StartPos = 1
SET #p_SourceText = RTRIM( LTRIM( #p_SourceText))
SET #w_Length = DATALENGTH( RTRIM( LTRIM( #p_SourceText)))
SET #w_Delimeter_Len = len(#p_Delimeter)
END
WHILE #w_Continue = 1
BEGIN
SET #w_Delimeter_pos = CHARINDEX( #p_Delimeter
,(SUBSTRING( #p_SourceText, #w_StartPos
,((#w_Length - #w_StartPos) + #w_Delimeter_Len)))
)
IF #w_Delimeter_pos > 0 -- delimeter(s) found, get the value
BEGIN
SET #w_tmp_txt = LTRIM(RTRIM( SUBSTRING( #p_SourceText, #w_StartPos
,(#w_Delimeter_pos - 1)) ))
IF dbo.isReallyNumeric(#w_tmp_txt) = 1 --and not #w_tmp_txt in('.', '-', '+', '^')
BEGIN
--set #w_tmp_int = cast( cast(#w_tmp_txt as real) as bigint)--numeric) as bigint)
SET #w_tmp_int = CASE WHEN (CAST(#w_tmp_txt AS REAL) BETWEEN -9223372036854775808 AND 9223372036854775808) THEN CAST( CAST(#w_tmp_txt AS REAL) AS bigint) ELSE NULL END
SET #w_tmp_num = CAST( #w_tmp_txt AS REAL)--numeric(18,3))
END
ELSE
BEGIN
SET #w_tmp_int = NULL
SET #w_tmp_num = NULL
END
SET #w_StartPos = #w_Delimeter_pos + #w_StartPos + (#w_Delimeter_Len- 1)
END
ELSE -- No more delimeters, get last value
BEGIN
SET #w_tmp_txt = LTRIM(RTRIM( SUBSTRING( #p_SourceText, #w_StartPos
,((#w_Length - #w_StartPos) + #w_Delimeter_Len)) ))
IF dbo.isReallyNumeric(#w_tmp_txt) = 1 --and not #w_tmp_txt in('.', '-', '+', '^')
BEGIN
--set #w_tmp_int = cast( cast(#w_tmp_txt as real) as bigint)--as numeric) as bigint)
SET #w_tmp_int = CASE WHEN (CAST(#w_tmp_txt AS REAL) BETWEEN -9223372036854775808 AND 9223372036854775808) THEN CAST( CAST(#w_tmp_txt AS REAL) AS bigint) ELSE NULL end
SET #w_tmp_num = CAST( #w_tmp_txt AS REAL)--numeric(18,3))
END
ELSE
BEGIN
SET #w_tmp_int = NULL
SET #w_tmp_num = NULL
END
SELECT #w_Continue = 0
END
INSERT INTO #tmpTable VALUES( #w_tmp_int, #w_tmp_num, #w_tmp_txt )
END
INSERT INTO #retTable SELECT Position2, Int_Value ,Num_value ,txt_value FROM #tmpTable
RETURN
END
Here are the supporting functions for above as well:
CREATE FUNCTION dbo.isReallyInteger
(
#num VARCHAR(64)
)
RETURNS BIT
BEGIN
IF LEFT(#num, 1) = '-'
SET #num = SUBSTRING(#num, 2, LEN(#num))
RETURN CASE
WHEN PATINDEX('%[^0-9-]%', #num) = 0
AND CHARINDEX('-', #num) <= 1
AND #num NOT IN ('.', '-', '+', '^')
AND LEN(#num)>0
AND #num NOT LIKE '%-%'
THEN
1
ELSE
0
END
END
CREATE FUNCTION dbo.isReallyNumeric
(
#num VARCHAR(64)
)
RETURNS BIT
BEGIN
IF LEFT(#num, 1) = '-'
SET #num = SUBSTRING(#num, 2, LEN(#num))
DECLARE #pos TINYINT
SET #pos = 1 + LEN(#num) - CHARINDEX('.', REVERSE(#num))
RETURN CASE
WHEN PATINDEX('%[^0-9.-]%', #num) = 0
AND #num NOT IN ('.', '-', '+', '^')
AND LEN(#num)>0
AND #num NOT LIKE '%-%'
AND
(
((#pos = LEN(#num)+1)
OR #pos = CHARINDEX('.', #num))
)
THEN
1
ELSE
0
END
END
Usage Examples:
--Single Character Delimiter
--select * from dbo.ParseText2Table('100|120|130.56|Yes|Cobalt|Blue','|')
--select txt_value from dbo.ParseText2Table('100 120 130.56 Yes Cobalt Blue',' ') where Position = 3
--select * from dbo.ParseText2Table('100,120,130.56,Yes,Cobalt,Blue,,',',')
/*
POSITION Int_Value Num_value txt_value
----------- ----------- -------------------- --------------
1 100 100.000 100
2 120 120.000 120
3 131 130.560 130.56
4 NULL NULL Yes
5 NULL NULL Cobalt Blue
*/

Split data from 2 symbols

have table
CREATE TABLE #tbl
(
id int identity(1,1),
obj_type int ,
obj_id nvarchar(50)
)
have data like : 153:0|114:0|147:0|148:0|152:0|155:0
want insert which data ise before " : " to obj_id , which data is next to " : " insert tu obj_type. it's must be like
id obj_type obj_id
1 0 153
2 0 114
3 0 147
4 0 148
5 0 152
6 0 155
How do it in stored procedure ? not function
declare #S varchar(100) = '153:0|114:0|147:0|148:0|152:0|155:0'
declare #xml xml
select #xml = '<item><value>'+replace(replace(#s, ':','</value><value>'), '|','</value></item><item><value>')+'</value></item>'
select N.value('value[1]', 'int') as obj_id,
N.value('value[2]', 'int') as obj_type
from #xml.nodes('item') as T(N)
SQL Fiddle
You can wait for some experts answer
till then you can give it one chance
insert into #tbl
SELECT LEFT(splitdata, CHARINDEX(':', splitdata) - 1) AS obj_id,
RIGHT(splitdata, CHARINDEX(':', REVERSE(splitdata)) - 1) AS obj_type from (select splitdatafrom fnSplitString(parameterName,'|')
now you can write stringsplit function like this
CREATE FUNCTION [dbo].[fnSplitString]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(splitdata NVARCHAR(MAX)
)
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output (splitdata)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
Another Solution :
Create FUNCTION [dbo].[SplitString]
(
#List NVARCHAR(MAX),
#Delim VARCHAR(255)
)
RETURNS TABLE
AS
RETURN ( SELECT [Value] FROM
(
SELECT
[Value] = LTRIM(RTRIM(SUBSTRING(#List, [Number],
CHARINDEX(#Delim, #List + #Delim, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects) AS x
WHERE Number <= LEN(#List)
AND SUBSTRING(#Delim + #List, [Number], LEN(#Delim)) = #Delim
) AS y
);
taken form: T-SQL split string
And then select the values:
Declare
#Text varchar (100) = '153:0|114:0|147:0|148:0|152:0|155:0',
#Delim varchar(50) = ':0|'
select case when CHARINDEX(':0', Value) > 0 then Left(Value, Len(Value)-2) else Value End AS Result from dbo.SplitString(#Text, #Delim)
CREATE procedure [dbo].[Insert_procedure]
#inputString varchar(max)
AS
BEGIN
set #inputString ='2153:770|114:0|147:0|148:0|152:0|155:0' Declare #delimiter char(1) = '|' Declare #delimiter_Colon char(1) = ':'
DECLARE #chIndex int DECLARE #chIndex1 int DECLARE #item varchar(100)Declare #ReverseString varchar(max)
SELECT #ReverseString = Reverse(substring(reverse(#inputString), 1, 1))
IF(#ReverseString <> '|')
set #inputString = #inputString +'|'
WHILE CHARINDEX(#delimiter, #inputString, 0) <> 0
BEGIN
SET #chIndex = CHARINDEX(#delimiter, #inputString, 0)
SELECT #item = SUBSTRING(#inputString, 1, #chIndex - 1)
IF LEN(#item) > 0
BEGIN
set #chIndex1 = CHARINDEX(#delimiter_Colon, #item, 0)
Declare #obj_type int Declare #obj_id varchar(50)
SELECT #obj_id = SUBSTRING(#item, #chIndex1+1,len(#item)) SELECT #obj_type = SUBSTRING(#item,1,#chIndex1-1)
Insert into TEST(obj_type,obj_id) values (#obj_type,#obj_id)
END
SELECT #inputString = SUBSTRING(#inputString, #chIndex + 1, LEN(#inputString))
END
END

Have someone a function for SQL Server that given a pattern and a value could return a normalized value

I'm programming a function in SQL 2008R2 that i could give it some parameters like a value varchar, a pattern varchar, a separator char and a filler also char. Then I would like to give the value '22687' with the patter '000.000.000.000', a separator '.' and the filler would be '0', then i would like to expect the function will return '000.000.022.687', does any one have a function already done that can do this?
Something like this:
DECLARE #valor VARCHAR(30)
DECLARE #formato VARCHAR(30)
DECLARE #separador CHAR(1)
DECLARE #rellenarcon CHAR(1)
SELECT #valor = '22959'
SELECT #formato = '000.000.000.000'
SELECT #separador = '.'
SELECT #rellenarcon = '0'
DECLARE #n INTEGER
DECLARE #m INTEGER
DECLARE #i INTEGER
DECLARE #j INTEGER
SELECT #n = LEN(#formato)
SELECT #m = LEN(#valor)
SELECT #i = 1
SELECT #j = 1
DECLARE #res2 varchar(30)
SELECT #res2 = ''
SELECT #valor = REVERSE(#valor)
WHILE #i<=#n
BEGIN
if SUBSTRING(#formato,#i,1) <> #separador
begin
IF #j<=#m
BEGIN
SELECT #res2 = #res2 + SUBSTRING(#valor,#j,1)
SELECT #i=#i+1
SELECT #j=#j+1
END
ELSE
BEGIN
SELECT #res2 = #res2 + #rellenarcon
SELECT #i=#i+1
END
end
else
BEGIN
SELECT #res2 = #res2 + #separador
SELECT #i=#i+1
END
END
print reverse(#res2)
Is a crossover code from java to tsql, the original code in java is:
public static String formatear(String valor, String formato, char separator,
char fillWith, Map<Integer, String> params) {
int n = formato.length() - 1;
int m = valor.length() - 1;
int i = n;
int j = m;
StringBuilder res = new StringBuilder(formato);
for(; i >= 0; i--) {
if(res.charAt(i) != separator) {
if(j >= 0) {
res.deleteCharAt(i);
res.insert(i, valor.charAt(j--));
} else {
res.deleteCharAt(i);
res.insert(i, fillWith);
}
}
}
if(params != null) {
Set<Integer> keys = params.keySet();
for(Integer key : keys) {
i = key;
res.deleteCharAt(i);
res.insert(i, params.get(key));
}
}
return res.toString();
}
The following assumes well-formed inputs, e.g. the value is not longer than the pattern.
declare #Pattern as VarChar(64) = '000.000.000.000';
declare #Fill as Char = '0';
declare #Value as VarChar(64) = '22687';
declare #False as Bit = 0;
declare #True as Bit = 1;
with Gargoyle as (
select #Pattern as Pattern, #Value as Value, Cast( '' as VarChar(64) ) as Buffer,
case when Right( #Pattern, 1 ) = #Fill then #True else #False end as Fill
union all
select
-- Always consume a character from the pattern.
Left( Pattern, Len( Pattern ) - 1 ),
-- Consume a character from the value if the pattern contains fill at the current position.
case
when Fill = #True and Value != '' then Left( Value, Len( Value ) - 1 )
else Value end,
-- Add the correct character to the buffer.
Cast( case when Fill = #True and Value != '' then Right( Value, 1 ) else Right( Pattern, 1 ) end + Buffer as VarChar(64) ),
-- Check the next pattern character for fill.
case
when Len( Pattern ) = 1 then #False
when Substring( Pattern, Len( Pattern ) - 1, 1 ) = #Fill then #True
else #False end
from Gargoyle
where Pattern != ''
)
select Buffer
from Gargoyle
where Pattern = '';
Or, as a function:
create function dbo.PatternFill( #Pattern as VarChar(64), #Fill as Char, #Value as VarChar(64) )
returns VarChar(64)
as
begin
declare #Buffer as VarChar(64) = ''
declare #PatternChar as Char = Right( #Pattern, 1 )
declare #ValueChar as Char = Right( #Value, 1 )
while #Pattern != ''
begin
if #PatternChar = #Fill and #ValueChar != ''
begin
-- Replace a fill character with a value character.
select #Buffer = #ValueChar + #Buffer
if Len( #Value ) > 1
select #Value = Left( #Value, Len( #Value ) - 1 ), #ValueChar = Right( #Value, 1 )
else
select #ValueChar = '', #Value = ''
end
else
begin
-- Copy the pattern character.
select #Buffer = #PatternChar + #Buffer
end
if Len( #Pattern ) > 1
select #Pattern = Left( #Pattern, Len( #Pattern ) - 1 ), #PatternChar = Right( #Pattern, 1 )
else
select #PatternChar = '', #Pattern = ''
end
return #Buffer
end
go
declare #Result as VarChar(64)
declare #Count as Int = 1000000
declare #Start as DateTime = GetDate()
while #Count > 0
select #Result = dbo.PatternFill( '000.000.000.000', '0', '22687' ), #Count = #Count - 1
select #Result as [Result], DateDiff( ms, #Start, GetDate() ) as [Total ms]
1,000,000 iterations on my notebook took 151,656ms, but it's busy BOINCing. That's simple timing with no correction for the time consumed by an empty loop or calling an empty function.
This query will do it:
;with cteZeroPadded(Num) as
(
Select Right('000000000000' + '22687', 12)
)
,cteSplit as
(
Select SUBSTRING(Num, 1, 3) Col1
,SUBSTRING(Num, 4, 3) Col2
,SUBSTRING(Num, 7, 3) Col3
,SUBSTRING(Num, 10, 3) Col4
From cteZeroPadded
)
Select Col1 + '.' + Col2 + '.' + Col3 + '.' + Col4
From cteSplit
In SQLServer2005+ you can use option with recursive CTE
DECLARE #valor varchar(30) = '22959',
#formato varchar(30) = '000000000000000',
#text varchar(30),
#result varchar(30) = N''
SET #text = REVERSE(RIGHT(#formato + #valor, 15))
;WITH cte AS
(
SELECT 1 AS Number, SUBSTRING(#text, 1, 1) AS Num
UNION ALL
SELECT c.Number + 1,
CASE WHEN c.Number IN(3, 7, 11) THEN '.' ELSE
SUBSTRING(#text, CASE WHEN c.Number > 11 THEN c.Number - 2
WHEN c.Number > 7 THEN c.Number - 1
WHEN c.Number > 3 THEN c.Number
ELSE c.Number + 1
END, 1)
END
FROM cte c
WHERE Number < LEN(#text)
)
SELECT #result += c.Num
FROM cte c
ORDER BY Number DESC
SELECT #result
See demo on SQLFiddle

Split value pairs and a create table using UDF

I've been trying to write a Table-Valued function that takes value pairs as a parameter and return a table with two columns.
Below is the function signature I am trying to do.
FUNCTION [dbo].[ValuePairParser]( #DelimitedValuePairs VARCHAR(MAX),
#Delimiter CHAR(1),
#ValuePairDelimiter CHAR(1) )
RETURNS #ValuePairTable
TABLE ( Id INT, Code INT )
I want to call the method like below
#ValuePairs VARCHAR(MAX) = '1:1, 1:2, 1:4, 2:3, 1000:230, 130:120,'
ValuePairParser (#ValuePairs, ',', ':')
Can you see any nice way to split above ValuePairs sting and create a table with two columns?
CREATE FUNCTION [dbo].[SplitWithPairs]
(
#List NVARCHAR(MAX),
#MajorDelimiter VARCHAR(3) = ',',
#MinorDelimiter VARCHAR(3) = ':'
)
RETURNS #Items TABLE
(
Position INT IDENTITY(1,1) NOT NULL,
LeftItem INT NOT NULL,
RightItem INT NOT NULL
)
AS
BEGIN
DECLARE
#Item NVARCHAR(MAX),
#LeftItem NVARCHAR(MAX),
#RightItem NVARCHAR(MAX),
#Pos INT;
SELECT
#List = #List + ' ',
#MajorDelimiter = LTRIM(RTRIM(#MajorDelimiter)),
#MinorDelimiter = LTRIM(RTRIM(#MinorDelimiter));
WHILE LEN(#List) > 0
BEGIN
SET #Pos = CHARINDEX(#MajorDelimiter, #List);
IF #Pos = 0
SET #Pos = LEN(#List) + LEN(#MajorDelimiter);
SELECT
#Item = LTRIM(RTRIM(LEFT(#List, #Pos - 1))),
#LeftItem = LTRIM(RTRIM(LEFT(#Item,
CHARINDEX(#MinorDelimiter, #Item) - 1))),
#RightItem = LTRIM(RTRIM(SUBSTRING(#Item,
CHARINDEX(#MinorDelimiter, #Item)
+ LEN(#MinorDelimiter), LEN(#Item))));
INSERT #Items(LeftItem, RightItem)
SELECT #LeftItem, #RightItem;
SET #List = SUBSTRING(#List,
#Pos + LEN(#MajorDelimiter), DATALENGTH(#List));
END
RETURN;
END
GO
DECLARE #ValuePairs VARCHAR(MAX) = '1:1, 1:2, 1:4, 2:3,1000:230, 130:120,';
SELECT LeftItem, RightItem
FROM dbo.SplitWithPairs(#ValuePairs, ',', ':')
ORDER BY Position;
GO
create function ValuePairParser(#DelimitedValuePairs varchar(MAX),
#Delimiter char(1),
#ValuePairDelimiter char(1))
returns #ValuePairTable table(Id int, Code int) as
begin
with Split(ValuePair, Rest) as
(
select left(#DelimitedValuePairs, charindex(#Delimiter, #DelimitedValuePairs)-1),
stuff(#DelimitedValuePairs, 1, charindex(#Delimiter, #DelimitedValuePairs), '')
where charindex(#Delimiter, #DelimitedValuePairs) > 0
union all
select left(Rest, charindex(#Delimiter, Rest)-1),
stuff(Rest, 1, charindex(#Delimiter, Rest), '')
from Split
where charindex(#Delimiter, Rest) > 0
)
insert into #ValuePairTable
select left(ValuePair, charindex(#ValuePairDelimiter, ValuePair)-1),
stuff(ValuePair, 1, charindex(#ValuePairDelimiter, ValuePair), '')
from Split
option (maxrecursion 0)
return
end