IBM Informix aggregate function - sql

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!

Related

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

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

Count unknown variables from a table

I have a problem here... if I have a table with few repeated string results. I want to know the value am the ammount of each.
For example. A function return an unknown "letters" and with unknown quantities in quantity
Function () return Table end
Table ={'a','a','c','b','b','a',...}
And I want to get this.
table.a={'a','a','a'}
table.b={'b','b'}
table.c={'c'}
....
....
I have no clue how to solve it...
Write a function, which creates a hash map of these things:
function RepetitionCounter(tInput)
local tCounter = {}
for i, v in ipairs(tInput) do
tCounter[v] = (tCounter[v] or 0) + 1
end
return tCounter
end
which you'll use as follows:
local tData = {'a','a','c','b','b','a',...}
local tCounts = RepetitionCounter(tData)
and the table tCounts would be as follows:
tCounts.a = 3
tCounts.b = 2
tCounts.c = 1
Modifying the function above by just a little, you can get the desired output. Replace the following line:
tCounter[v] = (tCounter[v] or 0) + 1
with
if not tCounter[v] then
tCounter[v] = {}
else
table.insert(tCounter[v], v)
end

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