How can I output text until it is ascii in SQL? - sql

How can I output text until it is ascii in SQL?
Here is what I am trying to do:
DECLARE #input VARCHAR(20)
SET #input = 'text'
DECLARE #index INT
SET #index = 1
DECLARE #output VARCHAR(32)
SET #output = ''
WHILE CHAR(ASCII(SUBSTRING(#input, #index, 1))) LIKE '[ -~]'
BEGIN
SET #output = #output + CHAR(ASCII(SUBSTRING(#input, #index, 1)))
SET #index = #index + 1
END
SELECT #output
But in the end I am getting an empty string. Why? What am I missing here?
I am expecting the value of the #output to be 'text' in the end of the script.
UPDATE
If I update the script to the
DECLARE #input VARCHAR(20)
SET #input = 'text'
DECLARE #index INT
SET #index = 1
DECLARE #output VARCHAR(32)
SET #output = ''
WHILE CHAR(ASCII(SUBSTRING(#input, #index, 1))) LIKE '[a-b]'
BEGIN
SET #output = #output + CHAR(ASCII(SUBSTRING(#input, #index, 1)))
SET #index = #index + 1
END
SELECT #output
It will work as expected. But here I just shrinked the set from all printable ascii characters to only small letters. Why does the shrinked set [a-b] include the text characters and the extended set [ -~] does not?

A space is not a valid range delimiter so LIKE [ -~] will not work. That becomes a test for those three characters only.
You could check the ASCII code directly, rather than using LIKE:
DECLARE #input VARCHAR(20)
SET #input = 'text'
DECLARE #index INT
SET #index = 1
DECLARE #output VARCHAR(32)
SET #output = ''
WHILE ASCII(SUBSTRING(#input, #index, 1)) BETWEEN 32 AND 126
BEGIN
SET #output = #output + CHAR(ASCII(SUBSTRING(#input, #index, 1)))
SET #index = #index + 1
END
SELECT #output
demo
Side note: It's possible to get unexpected results with LIKE ranges because the comparison is collation-aware.
Answer copied from the duplicate question on Database Administrators

Related

Check a chunk of text for tilde and ensure they are 60 characters apart. If not place a tilde inbetween

I currently have a function in SQL that places a tilde every 60 characters, but the original text already has tilde's so , basically I want to change that if there is a tilde and the next tilde is under 60 characters away, then skip to the next tilde. If its over 60 characters, then only then place a extra tilde.
My current function looks like;
function [dbo].[AddTilde] (
#string varchar(max),
#count int
)
returns varchar(max)
as
begin
declare #result varchar(max) = ''
declare #token varchar(max) = ''
while DATALENGTH(#string) > 0
begin
select #token = left(#string, #count)
select #string = REPLACE(#string, #token, '')
select #result +=#token +case when DATALENGTH(#string)=0 then '' else '~' end
end
return #result
end
Any help appreciated
Many Thanks
DECLARE #string1 VARCHAR(max),
#string2 VARCHAR(max) = '',
#i1 INT,
#i2 INT
SET #string1 = '12345678901234567890~1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'
WHILE LEN(#string1) > 0
BEGIN
SET #i1 = CHARINDEX('~',#string1)
SET #i2 = #i1 - (ABS(#i1-60) + (#i1-60)) / 2 -- MINIMUM OF ~ LOCATION AND 60
SET #i1 = LEN(#string1)
IF #i2 = 0 SET #i2 = #i1 - (ABS(#i1-60) + (#i1-60)) / 2 -- MINIMUM OF LENGTH OF #string1 LOCATION AND 60
IF #i2 < 60
BEGIN
SET #string2 = #string2 + LEFT(#string1,#i2)
SET #string1 = RIGHT(#string1,#i1-#i2)
END
ELSE
BEGIN
SET #string2 = #string2 + LEFT(#string1,60) + '~'
SET #string1 = RIGHT(#string1,#i1-60)
END
END
Results: 12345678901234567890~123456789012345678901234567890123456789012345678901234567890~1234567890123456789012345678901234567890
This is based on my String Splitting function, which has a very good performance.
This function should be quite efficient, albeit hard to understand (I have added a few commenets in attempt to make it easier).
You can change internal parameters easily e.g. #Delimitor can be multiple characters
Test cases are included at the bottom.
ALTER FUNCTION [dbo].[AddTilde]
(
#String VARCHAR( MAX ),
#Count INT
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #CurrentPosition BIGINT, #NextDelimiterPosition BIGINT, #NextFixedPosition BIGINT, #NextPosition BIGINT,
#DelimiterLength INT, #Delimiter VARCHAR( 5 ), #Result VARCHAR( MAX )
-- Initialise
SET #Delimiter = '~'
SET #DelimiterLength = LEN( #Delimiter )
SET #Result = ''
-- Ensures we can terminate loop without using an extra IF condition, saves a few = 0 checks
SET #String = #String + #Delimiter
SET #CurrentPosition = 1
-- Check for next Tilde position
SET #NextDelimiterPosition = CHARINDEX( #Delimiter, #String, #CurrentPosition )
-- Initialise fixed increment position
SET #NextFixedPosition = #Count
-- Compare, which one is earlier and use that one. Handle cases where the next token begins with Tilde
SET #NextPosition = CASE WHEN #NextDelimiterPosition - #DelimiterLength > #NextFixedPosition THEN #NextFixedPosition ELSE #NextDelimiterPosition END
WHILE #NextDelimiterPosition > 0
BEGIN
SET #Result = #Result + SUBSTRING( #String, #CurrentPosition, #NextPosition - #CurrentPosition + 1 )
-- Handle cases where the next token begins with Tilde and avoids doubling up Tildes
+ ( CASE WHEN #NextPosition = #NextDelimiterPosition THEN '' ELSE #Delimiter END )
SET #CurrentPosition = #NextPosition + 1
-- Increment fixed position
SET #NextFixedPosition = #CurrentPosition + #Count - 1
-- Check for next Tilde position
SET #NextDelimiterPosition = CHARINDEX( #Delimiter, #String, #CurrentPosition )
SET #NextPosition = CASE WHEN #NextDelimiterPosition - #DelimiterLength > #NextFixedPosition THEN #NextFixedPosition ELSE #NextDelimiterPosition END
END
-- Remove trailing Tilde
SET #Result = SUBSTRING( #Result, 1, LEN( #Result ) - #DelimiterLength )
RETURN #Result
END
/* Test Cases
SELECT dbo.[AddTilde]( 'ab~c~defghijkl~', 3 ) --> 'ab~c~def~ghi~jkl~'
SELECT dbo.[AddTilde]( '~ab~c~defghijkl', 3 ) --> '~ab~c~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'ab~c~~defghijkl', 3 ) --> 'ab~c~~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'abcdefghijkl', 3 ) --> 'abc~def~ghi~jkl'
SELECT dbo.[AddTilde]( 'a', 3 ) --> 'a'
*/

How to find the First Character starting in SQL Server 2008

DECLARE #input NVARCHAR(MAX) = 'Create Procedure Test'
DECLARE #SearhString NVARCHAR(MAX) = 'Procedure'
DECLARE #index BIGINT = CHARINDEX(#keyword, #input)
1- I want to find the length of the next character starting after string Procedure
2- After that I want to check if the dbo string exists after that length to the specified location.
Suppose the first character Test starting at position 14 after string Procedure, so I have to use
DECLARE #String varchar(20) = SUBSTRING(#input, 21, 10)
Declare #StringIndex BIGINT = CHARINDEX('dbo.', #String)
Declare #FirstCharacterIndex BIGINT(This will be the index of the first character)
If #String not contains string dbo then stuff MySchema before string Test.
Please suggest.
UPDATE
I have updated my question 22 is the result of #index + #FirstCharacterIndex + 9
#index = 8
#FirstCharacterIndex = 4(3 count for spaces and 4th is for first character)
9 = No. of characters of string Procedure
try this sql below:
DECLARE #input NVARCHAR(MAX) = 'Create Procedure Test';
DECLARE #input2 NVARCHAR(MAX) = 'Create Procedure dbo Test';
DECLARE #SearchString NVARCHAR(MAX) = 'Procedure';
DECLARE #index BIGINT = CHARINDEX(#SearchString, #input);
select #input, #SearchString as searchstring, #index as Index_SearchString , #index +len(#SearchString) as Last_CharacterOfSearchString
, charIndex('Test', #input,#index +len(#SearchString)) as test_Index
, charIndex('dbo', #input,#index +len(#SearchString)) as dbo_Index
, substring(#input, 0, #index +len(#SearchString)) as str1
, substring(#input, #index +len(#SearchString), len(#input)-#index +len(#SearchString)) as str2
Declare #dbo_Index int
Declare #str1 nvarchar(max), #str2 nvarchar(max)
set #dbo_Index = charIndex('dbo', #input,#index +len(#SearchString))
--select #dbo_Index
if #dbo_Index = 0
begin
set #str1 = substring(#input, 0, #index +len(#SearchString))
set #str2 = substring(#input, #index +len(#SearchString), len(#input)-#index +len(#SearchString))
select #str1 + ' ' + 'MySchema' + #str2
end
--,charIndex('dbo',#input2, #index +len(#SearchString)) as dbo_Index
Updated:
objective: Get the "Under Fire" first character index ("U")
DECLARE #input2 NVARCHAR(MAX) = 'Create Procedure Under Fire Test';
DECLARE #SearchString NVARCHAR(MAX) = 'Procedure';
--,charIndex('dbo',#input2, #index +len(#SearchString)) as dbo_Index
select substring(#input2,17,25)
, CHARINDEX(#SearchString,#input2,0) + len(#SearchString) as LastIndexOf_Procedure
, CHARINDEX('Test',#input2,0)
,len(#input2)
,substring(#input2, CHARINDEX(#SearchString,#input2,0) + len(#SearchString), CHARINDEX('Test',#input2,0)- (CHARINDEX(#SearchString,#input2,0)+len(#SearchString)))
set #SearchString= Ltrim(rtrim(substring(#input2, CHARINDEX(#SearchString,#input2,0) + len(#SearchString), CHARINDEX('Test',#input2,0)- (CHARINDEX(#SearchString,#input2,0)+len(#SearchString)))))
select #SearchString
select
#input2
,#SearchString as SearchString
,CHARINDEX(#SearchString,#input2,0) as FirstCharacterIndex

SQL - Replace non numeric characters in string

I'm writing a function that takes a string and has to replace any non-numeric character with two numeric characters taken from a table. This is the code I use to explore the string and find the non-numeric characters:
SET #string = '1a2b3c4d'
SET #wcount= 0
SET #index = 1
SET #len= LEN(#string)
WHILE #index<= #len
BEGIN
SET #char = SUBSTRING(#string, #index, 1)
IF #char LIKE '%[a-z]%'
PRINT 'char ' + CONVERT(varchar(10), #index)
ELSE
PRINT #char
SET #index= #index+ 1
END
The output is the following
1
char 2
2
char 4
3
char 6
4
char 8
Now, when I find a non-numeric character I have to replace it with two numeric chars taken from a table by a select. E.g.
SELECT #temp = REPLACEMENT FROM Conversion_Tab WHERE EXPR = #char
In conclusion, if I have the following string '1a2a3a4a' and the replacement for 'a' is '88' the resulting string should be '188288388488'
Thanks in advance for any help.
Bye!
Try this
DECLARE #string VARCHAR(100)
DECLARE #outstring VARCHAR(100)
DECLARE #wcount INT
DECLARE #temp INT
DECLARE #index INT
DECLARE #len INT
DECLARE #char CHAR
SET #string = '1a2a3a4a'
SET #wcount= 0
SET #index = 1
SET #len= LEN(#string)
SET #outstring = ''
WHILE #index<= #len
BEGIN
SET #char = SUBSTRING(#string, #index, 1)
IF #char LIKE '%[a-z]%'
BEGIN
SELECT #temp = REPLACEMENT FROM #Conversion_Tab WHERE EXPR = #char
SET #outstring = #outstring + CONVERT(VARCHAR(10),#temp)
END
ELSE
BEGIN
SET #outstring = #outstring + #char
END
SET #index= #index+ 1
END
SELECT #outstring
SQL FIDDLE DEMO
looks like you need isnumeric(). so if not isnumeric(char) replace it with your lookup value.
taken from the other answer but produces the same result
DECLARE #string VARCHAR(100)
DECLARE #outstring VARCHAR(100)
DECLARE #wcount INT
DECLARE #temp INT
DECLARE #index INT
DECLARE #len INT
DECLARE #char CHAR
SET #string = '1a2a3a4a'
SET #wcount= 0
SET #index = 1
SET #len= LEN(#string)
SET #outstring = ''
WHILE #index<= #len
BEGIN
SET #char = SUBSTRING(#string, #index, 1)
IF ISNUMERIC(#char) = 0
BEGIN
SELECT #temp = REPLACEMENT FROM #Conversion_Tab WHERE EXPR = #char
SET #outstring = #outstring + CONVERT(VARCHAR(10),#temp)
END
ELSE
BEGIN
SET #outstring = #outstring + #char
END
SET #index= #index+ 1
END

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'));

get specific part of a string, by index?

I have a string like:
#TempCol = sigma_x1,sigma_x2,...,sigma_xd,XX,YY,ZZ
So how could I get a specific part of that string, based on, lets say an index.
so
if index is 0, get sigma_x1
if index is 1, get sigma_x2
if index is 2, get sigma_x3
if index is d-1,get sigma_xd
if index is d, get XX,
if index is d+1,get YY
and so on.
Previously Andriy M solved a similar issue, his code gets a substring based on a nomber but returns a substring the following way:
if #d is 1,get sigma_x1
if #d is 2,get sigma_x1,sigma_x2
if #d is 3,get sigma_x1,sigma_x2,sigma_x3
if #d is 4,get sigma_x1,sigma_x2,sigma_x3,sigma_x4
if #d is d,get sigma_x1,sigma_x2,sigma_x3,sigma_x4,...,sigma_xd (ALL THE STRING)
How to update this procedure to get specific element?
DECLARE #TempCol varchar(max), #d int, #p int, #Result varchar(max);
SET #TempCol = 'item1,item2,itemA,itemB,item#,item$';
SET #d = 3;
SET #p = 1;
WHILE #d > 0 AND #p > 0 BEGIN
SET #p = CHARINDEX(',', #TempCol, #p);
IF #p > 0 SET #p = #p + 1;
SET #d = #d - 1;
END;
IF #p = 0
SET #Result = #TempCol
ELSE
SET #Result = SUBSTRING(#TempCol, 1, #p - 2);
SELECT #Result;
just try this. hope this will meet your needs.
create a function GetIndex, which accepts string and delimiter to split the string
CREATE FUNCTION dbo.GetIndex(#String varchar(8000), #Delimiter char(1))
returns #temptable TABLE (itemindex int identity(1,1), items varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
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(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
and you can query like,
suppose you need 4th index then
select * from dbo.GetIndex(#TempCol,',') where itemindex = 4
to get an item of 4th index then
select items from dbo.GetIndex(#TempCol,',') where itemindex = 4
to get item to a variable
select #Aux = items from dbo.GetIndex(#TempCol,',') where itemindex = 4