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

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

Related

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.

IBM Informix aggregate function

I need to develop some kind of function in a informix db, in order to split one string into multiple rows for example:
Column1
one,two,three,four
And my expected result is:
column1
one
two
three
four
What i was thinking is to create a function, that splits the string into multiple rows. My actual code is the next one :
create function split(text_splitted varchar(100), separator char(1))
returning varchar(100)
define splitted_word varchar(100);
define current_val char(1);
define start, cont integer;
let start = 0;
let splitted_word = "";
let current_val = "";
for cont = 0 to length(text_splitted)
let current_val = substr(text_splitted, cont, 1);
if current_val = separator then
let splitted_word = substr(text_splitted, start, cont - start);
let start = cont + 1;
return splitted_word with resume;
end if;
end for;
end function
If you execute the next statement, works find:
execute function split('hello.my.name.is', '.');
And the result is:
hello
my
name
this is perfect, but my problem is that when you launch a query with this function, and the function returns more than one row an error is raised. What i have been google, is that i need to create an aggregate function but i am not able to build this function. I am new in this kind of developing....
Here is the little documentation i found: http://www.pacs.tju.edu/informix/answers/english/docs/dbdk/is40/extend/04aggs3.html
Thanks!

How to call procedure with package type param in oracle?

In oracle DB, I created a custom type in a package and i guess this type is similar to integer array.
create or replace PACKAGE mypackage AS
TYPE custom1 is table of integer index by binary_integer;
END mypackage;
Used type in procedure IN param and expecting out param to be size of IN param.
CREATE OR REPLACE PROCEDURE MYPROCEDURE( param1 in mypackage.custom1, count1 out integer) IS
begin
count1 := param.count();
END MYPROCEDURE
Now I want to call above procedure,for this I should prepare mypackage.custom1.
Please help me in constructing mypackage.custom1 and call above procedure.
You have some errors in your code;
CREATE OR REPLACE PACKAGE mypackage AS
TYPE custom1 IS TABLE OF INTEGER
INDEX BY BINARY_INTEGER;
END mypackage;
CREATE OR REPLACE PROCEDURE MYPROCEDURE(param1 IN mypackage.custom1, count1 OUT INTEGER) IS
BEGIN
count1 := param1.COUNT();
END MYPROCEDURE;
To call your procedure, you simply need to define two variables and call the procedure with them; for example, in an anonymous block:
declare
v mypackage.custom1;
n number;
begin
select 1
bulk collect into v
from dual connect by level <= 5;
--
MYPROCEDURE(v, n);
dbms_output.put_line('n= ' || n);
end;
n= 5
The same way, you can build your stored procedures, packages, ... to call your procedure.
Executing above procedure with list of integers passing to custom type
SET SERVEROUTPUT = ON;
declare
v mypackage.custom1;
n number;
begin
v(0) := 10;
v(1) := 12;
v(2) := 14;
v(3) := 16;
--
MYPROCEDURE(v, n);
dbms_output.put_line('n= ' || n);
end;
output :
n = 4
Here is the JDBC code to invoke above procedure
String procedure = "call MYPROCEDURE(?, ?)";
CallableStatement callableStatement = con.prepareCall(procedure);
ArrayDescriptor ad = ArrayDescriptor.createDescriptor("mypackage.custom1", con);
ARRAY arr = new ARRAY(ad, con, new Integer[]{1,2,3,4});
callableStatement.setArray(1, arr);
callableStatement.registerOutParameter(2, Types.INTEGER);
final boolean execute = callableStatement.execute();
System.out.println("No of entries :" + callableStatement.getObject(2));
output :
No of entries : 4

Netcool/OMNIbus SQL procedure not running\doing nothing

So i have a tool that call procedure.
Tool looks like that:
call Attach_test('select TTID from alerts.status where Class in (73000,8891) and to_int(TTID) > 0 and ServerSerial in ($selected_rows.Serial)',[ $selected_rows.Serial ]); flush iduc;
it should get TTID (that field have only one of many selected alarms ) and array of server serials of selected alarms.
Then all this data is transfered to SQL procedure that looks like:
declare
tempservser integer; k integer;
begin
for k = 1 to array_len(serserial) do
begin
set tempservser = serserial[k];
update alerts.status set ParentTT = parentttid, TTFlag = 2 where ServerSerial = tempservser and TTID = '' ;
end;
end
Parameters:
in parentttid Char(11)
in serserial array of Integer
And here comes the trouble - procedure do nothing. There is no errors or something but there is no update on selected alarms.
I want it to work like this - you select many alarms with only one that have TTID, run this tool that set ParentTT = TTID on every other of selected alarms.
OS ver. 8.1
Sorry for my english
I figured out how to do it:
Tool
call AttachSelectedToTTID([ $selected_rows.Serial ],[ $selected_rows.ParentTT ]);
flush iduc;
Procedure
declare
tempservser integer; k integer;n integer;partt char(15);
begin
for n = 1 to array_len(ttid) do
begin
if (ttid[n] != '' ) then
set partt = ttid[n];
end if;
end;
for k = 1 to array_len(serserial) do
begin
set tempservser = serserial[k];
update alerts.status set ParentTT = partt,TTFlag = 2 where Serial = tempservser and TTID = '';
end;
end
Parameters:
in ttid array of Char(15)
in serserial array of Integer

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