Modify characters of a string to their ASCII code plus 10, SQL - sql

I am using mariadb 10.3 and I'm trying to create a procedure that modifies all characters of a string, to each CHAR ASCII code, 10 position ahead.
I'm having trouble to find any functions to approach this problem, thanks.
For example given the string 'man':
ASCII codes: m = 109, a = 97, n = 110
Plus 10: 119 = w, 107 = k, 120 = x
So the function should return: 'wkx'

This works:
DECLARE longitud smallint;
DECLARE nuevaCadena varchar(100);
set longitud = LENGTH(cadena);
set nuevaCadena = '';
FOR i IN 1 .. longitud DO
set nuevaCadena = concat(nuevaCadena,char(ascii(substring(cadena,i)) + 10));
END FOR;

Related

Truncate decimals in string VB

I need only 4 decimals in a string.
Dim testString As String = "78.568134"
I want to truncate testString to = "78.5681"
Try the following methods based on your needs and modifications;
Try method 1)
Function TrimDigits(val As Double, Optional numdig As Integer = 2) As Double
Try
Return Fix(val * 10 ^ numdig) / 10 ^ numdig
Catch ex As Exception
Return 0
End Try
End Function
Try method 2)
truncatedNumber = Fix ( originalNumber * 100 ) / 100 'Truncate to 2 decimals.
truncatedNumber = Fix ( originalNumber * 1000 ) / 1000 'Truncate to 3 decimals.
truncatedNumber = Fix ( originalNumber * 10000 ) / 10000 'Truncate to 4 decimals.
Aside note
Fix() returns the integer portion of a number.
Example 01: Truncate 3.1415 to 2 decimals.
3.1415 x 100 equals 314.15
Fix(314.15) returns 314
314 / 100 equals 3.14
Example 02: Truncate 3.1415 to 3 decimals.
3.1415 x 1000 equals 3141.5
Fix(3141.5) returns 3141
3141 / 1000 equals 3.141
Int does not truncate negative numbers correctly. Int converts -8.4 to -9, and Fix converts -8.4 to -8. So Fix is the way to go
Code to find the index of the decimal point and truncate the string if there are more than 4 characters after it.
Dim stringLen As Integer = testString.Length
Dim decimalOffset As Integer = testString.IndexOf(".", 0)
If decimalOffset >= 0 Then
If stringLen > decimalOffset + 5 Then testString = testString.SubString(0, decimalOffset + 5)
End If
This is if you are only working with Strings. If you are working with Doubles there are other ways to do this.

how to generate a readable name in a SQL stored procedure?

In a stored procedure, I would like to generate a 'name' field in order to populate a dummy data table.
I use this and it works:
SET #nom = CONV(FLOOR(RAND() * 99999999999999), 20, 36);
But since this is for a name field, I would like to have only letters and not a mixture of letters and numbers.
I wanted to try something like this, but it doesn't work
SET #lenght=8+rand() *10; -- Définit une longueur aléatoire compris entre 8 et 18 caractères
SET#nom='';
WHILE #lenght>0 DO
#nom = CONCAT(#nom, char(round(rand()*25+65,0)));
SET #lenght=#lenght -1;
END WHILE;
do you have an idea?
Array-type variables cannot be used?
I could have taken a random character in an array variable on each iteration of the loop
SET #vowel= ('a','e','i', 'o','u','y');
For MySQL:
CREATE FUNCTION generate_random_word (len TINYINT UNSIGNED)
RETURNS VARCHAR(255)
BEGIN
DECLARE result VARCHAR(255) DEFAULT '';
REPEAT
SET result = CONCAT(result, CHAR(CEIL(RAND()*26+64)));
SET len = len - 1;
UNTIL !len END REPEAT;
RETURN result;
END
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=e2732456d0d418b1b181a4352c534119

SQL Removing Characters

I am using the below case statement
***, CASE WHEN Attributes LIKE ',Size = %,' Then right(Attributes, len(Attributes) - charindex('Size ', Attributes)) ELSE '' END as Size***
To get the below result
,Size = XXL ,Fits to Chest Size = 48 to 50 in,,Closure Type = Snap Button ,Material = Cotton ,Color = Khaki ,Sleeve Length = 33 in,Lining Material = Polyester Thread ,,Fabric Weight = 9 oz,,,Resists = Flame ,,,,,,,,,,,,,,,,,,,,,,,,,,
I want to remove everything and only have Size = XXL
Any help is appreciated.
If this is not an ETL process, then you should really consider changing your data model so you that you do not have to parse your Attribute column every time.
Below is a short solution with
STRING_SPLIT() (SQL Server 2016 and later)
TRIM() (SQL Server 2017 and later)
Sample data
create table CsvData
(
Attribute nvarchar(1000)
);
insert into CsvData (Attribute) values
('Foo = 10,Bar = 20,,,,,Size = XXL ,Fits to Chest Size = 48 to 50 in,,Closure Type = Snap Button ,Material = Cotton ,Color = Khaki ,Sleeve Length = 33 in,Lining Material = Polyester Thread ,,Fabric Weight = 9 oz,,,Resists = Flame ,,,,,,,,,,,,,,,,,,,,,,,,,,');
Solution
select trim(s.value) as SizeAttribute
from CsvData cd
cross apply string_split(cd.Attribute, ',') s
where left(s.value, 4) = 'Size';
Result
SizeAttribute
-------------
Size = XXL
Fiddle to see it in action.
Try This:
DECLARE #CsvData TABLE(Attribute nvarchar(1000));
insert into #CsvData (Attribute) values
('Foo = 10,Bar = 20,,,,,Size = XXL ,Fits to Chest Size = 48 to 50 in,,Closure Type =
Snap Button ,Material = Cotton ,Color = Khaki ,Sleeve Length = 33 in,Lining Material =
Polyester Thread ,,Fabric Weight = 9 oz,,,Resists = Flame ,,,,,,,,,,,,,,,,,,,,,,,,,,');
DECLARE #xml_value AS XML,
#string_value AS VARCHAR(2000),
#delimiter_value AS VARCHAR(15)
SET #string_value=(SELECT Attribute FROM #CsvData)
SET #delimiter_value =','
SET #xml_value = Cast(( '<studentname>'+ Replace(#string_value, #delimiter_value, '</studentname><studentname>') + '</studentname>' ) AS XML)
SELECT x.m.query('.').value('.', 'VARCHAR(15)') AS VALUE
FROM #xml_value.nodes('/studentname') AS x(m)
WHERE x.m.query('.').value('.', 'VARCHAR(15)') LIKE 'Size%'

Using levenshtein on parts of string in SQL

I am trying to figure out a way to work some fuzzy searching methods into our store front search field using the Levenshtein method, but I'm running into a problem with how to search for only part of product names.
For example, a customer searches for scisors, but we have a product called electric scissor. Using the Levenshtein method levenshtein("scisors","electric scissor") we will get a result of 11, because the electric part will be counted as a difference.
What I am looking for is a way for it to look at substrings of the product name, so it would compare it to levenshtein("scisors","electric") and then also levenshtein("scisors","scissor") to see that we can get a result of only 2 in that second substring, and thus show that product as part of their search result.
Non-working example to give you an idea of what I'm after:
SELECT * FROM products p WHERE levenshtein("scisors", p.name) < 5
Question: Is there a way to write an SQL statement that handles checking for parts of the string? Would I need to create more functions in my database to be able to handle it perhaps or modify my existing function, and if so, what would it look like?
I am currently using this implementation of the levenshtein method:
//levenshtein(s1 as VARCHAR(255), s2 as VARCHAR(255))
//returns int
BEGIN
DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT;
DECLARE s1_char CHAR;
-- max strlen=255
DECLARE cv0, cv1 VARBINARY(256);
SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), cv1 = 0x00, j = 1, i = 1, c = 0;
IF s1 = s2 THEN
RETURN 0;
ELSEIF s1_len = 0 THEN
RETURN s2_len;
ELSEIF s2_len = 0 THEN
RETURN s1_len;
ELSE
WHILE j <= s2_len DO
SET cv1 = CONCAT(cv1, UNHEX(HEX(j))), j = j + 1;
END WHILE;
WHILE i <= s1_len DO
SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = UNHEX(HEX(i)), j = 1;
WHILE j <= s2_len DO
SET c = c + 1;
IF s1_char = SUBSTRING(s2, j, 1) THEN
SET cost = 0; ELSE SET cost = 1;
END IF;
SET c_temp = CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + cost;
IF c > c_temp THEN SET c = c_temp; END IF;
SET c_temp = CONV(HEX(SUBSTRING(cv1, j+1, 1)), 16, 10) + 1;
IF c > c_temp THEN
SET c = c_temp;
END IF;
SET cv0 = CONCAT(cv0, UNHEX(HEX(c))), j = j + 1;
END WHILE;
SET cv1 = cv0, i = i + 1;
END WHILE;
END IF;
RETURN c;
END
This is a bit long for a comment.
First, I would suggest using a full-text search with a synonyms list. That said, you might have users with really bad spelling abilities, so the synonyms list might be difficult to maintain.
If you use Levenshtein distance, then I suggest doing it on a per word basis. For each word in the user's input, calculate the closest word in the name field. Then add these together to get the best match.
In your example, you would have these comparisons:
levenshtein('scisors', 'electric')
levenshtein('scisors', 'scissor')
The minimum would be the second. If the user types multiple words, such as 'electrk scisors', then you would be doing
levenshtein('electrk', 'electric') <-- minimum
levenshtein('electrk', 'scissor')
levenshtein('scisors', 'electric')
levenshtein('scisors', 'scissor') <-- minimum
This is likely to be an intuitive way to approach the search.

Informix 4gl Split a String or Char

I wanted to know the Informix 4gl command to split a variable
such as
lv_var = variable01;variable02
into
lv_var01 = variable01
lv_var02 = variable02
Is there something in Informix 4gl that can do this.
In python I could do
lv_array = lv_var.split(";")
and use the variables from the array
It's possible with classic Informix 4gl with something like this...
define
p_list dynamic array of char(10)
main
define
i smallint,
cnt smallint,
p_str char(500)
let p_str = "a;b;c;d"
let cnt = toarray(p_str, ";")
for i = 1 to cnt
display p_list[i]
end for
end main
function toarray(p_str, p_sep)
define
p_str char(2000),
p_sep char(1),
i smallint,
last smallint,
ix smallint,
p_len smallint
let ix = 0
let p_len = length(p_str)
# -- get size of array needed
for i = 1 to p_len
if p_str[i] = p_sep then
let ix = ix + 1
end if
end for
if ix > 0 then
# -- we have more then one
allocate array p_list[ix + 1]
let ix = 1
let last = 1
for i = 1 to p_len
if p_str[i] = p_sep then
let p_list[ix] = p_str[last,i-1]
let ix = ix + 1
let last = i + 1
end if
end for
# -- set the last one
let p_list[ix] = p_str[last, p_len]
else
# -- only has one
allocate array p_list[1]
let ix = 1
let p_list[ix] = p_str
end if
return ix
end function
Out:
a
b
c
d
Dynamic array support requires IBM Informix 4GL 7.32.UC1 or higher
There isn't a standard function to do that. One major problem is returning the array. I'd probably write a C function to do the job, but in I4GL, it would look like:
FUNCTION nth_split_field(str, c, n)
DEFINE str VARCHAR(255)
DEFINE c CHAR(1)
DEFINE n INTEGER
...code to find nth field delimited by c in str...
END FUNCTION
What you'll find is that the products that have grown to superceed Informix 4GL over the years such as FourJs Genero will have built-in methods that have been added to simplify the Informix 4GL developers life.
So something like this would do what you are looking for if you upgraded to Genero
-- Example showing how string can be parsed using string tokenizer
-- New features added to Genero since Informix 4gl used include
-- STRING - like a CHAR but length does not need to be specified - http://www.4js.com/online_documentation/fjs-fgl-manual-html/?path=fjs-fgl-manual#c_fgl_datatypes_STRING.html
-- DYNAMIC ARRAY like an ARRAY but does not need to have length specified. Is also passed by reference to functions - http://www.4js.com/online_documentation/fjs-fgl-manual-html/?path=fjs-fgl-manual#c_fgl_Arrays_010.html
-- base.StringTokenizer - methods to split a string - http://www.4js.com/online_documentation/fjs-fgl-manual-html/?path=fjs-fgl-manual#c_fgl_ClassStringTokenizer.html
MAIN
DEFINE arr DYNAMIC ARRAY OF STRING
DEFINE i INTEGER
CALL string2array("abc;def;ghi",arr,";")
-- display result
FOR i = 1 TO arr.getLength()
DISPLAY arr[i]
END FOR
-- Should display
--abc
--def
--ghi
END MAIN
FUNCTION string2array(s,a,delimiter)
DEFINE s STRING
DEFINE a DYNAMIC ARRAY OF STRING
DEFINE delimiter STRING
DEFINE tok base.StringTokenizer
CALL a.clear()
LET tok = base.StringTokenizer.create(s,delimiter)
WHILE tok.hasMoreTokens()
LET a[a.getLength()+1] = tok.nextToken()
END WHILE
-- a is DYNAMIC ARRAY so has been pased by reference and does not need to be explicitly returned
END FUNCTION