Funciones para trabajar con Números Aleatorios (Random)

Si algo le falta al Informix (y al Posgres) son funciones para trabajar con números aleatorios. En algún momento de los más de 8 años de sufrimiento con este SGBD, tuve que codificar estas funciones, y me pareció una buena idea compartirlas… Explicaría una por una cada función, pero la verdad que creo no hace al tema, así que espero que alcance con los nombres de cada una y cualquier cosa preguntan al final del hilo.

drop procedure "dba".sp817_setrandomseed(decimal);
CREATE PROCEDURE sp817_SetRandomSeed(n DECIMAL(10) DEFAULT NULL)
   DEFINE GLOBAL seed DECIMAL(10) DEFAULT NULL;
   DEFINE hora integer;
   DEFINE minuto integer;
   DEFINE segundo integer;
   IF n IS NULL THEN
      let hora = current::datetime HOUR TO HOUR::char(2)::int;
      let minuto = current::datetime MINUTE TO MINUTE::char(2)::int;
      let segundo = current::datetime SECOND TO SECOND::char(2)::int;
      IF minuto>segundo THEN
         LET n = hora*minuto/(segundo+1);
      ELSE
         LET n = hora*segundo/(minuto+1);
      END IF;
   END IF;
   LET seed = n;
END PROCEDURE;                                                                                                                                                               
grant execute on procedure "dba".sp817_setrandomseed(decimal) to public as dba;
drop function "dba".sp817_getrandomseed();
CREATE FUNCTION sp817_GetRandomSeed() RETURNING DECIMAL(10)
   DEFINE GLOBAL seed DECIMAL(10) DEFAULT NULL;
   RETURN seed;
END FUNCTION;                                                                                                                  
grant execute on function "dba".sp817_getrandomseed() to public as dba;
drop function "dba".sp817_random();
CREATE FUNCTION sp817_Random() RETURNING FLOAT
   DEFINE GLOBAL seed DECIMAL(10) DEFAULT NULL;
   DEFINE d DECIMAL(20,0);
   DEFINE x1 FLOAT;
   DEFINE x2 FLOAT;
   IF seed is NULL THEN
      EXECUTE PROCEDURE sp817_SetRandomSeed();
   END IF;   
   LET d = (seed * 1103515245) + 12345;
   LET seed = d - 4294967296 * TRUNC(d / 4294967296);
   LET x1 = MOD(TRUNC(seed / 65536), 32768);
   LET d = (seed * 1103515245) + 12345;
   LET seed = d - 4294967296 * TRUNC(d / 4294967296);
   LET x2 = MOD(TRUNC(seed / 65536), 32768);
   IF x1 > x2 then
      RETURN x2/x1;
   ELSE
      RETURN x1/x2;
   END IF;
END FUNCTION;                                                                                                                                   
grant execute on function "dba".sp817_random() to public as dba;
drop function "dba".sp817_randomdesdehasta(integer,integer);
create function sp817_RandomDesdeHasta(desde integer, hasta integer) returning integer
   if hasta<desde then
      return cast(floor((desde-hasta+1)*sp817_random()) as integer) + hasta; 
   end if;
   return cast(floor((hasta-desde+1)*sp817_random()) as integer) + desde; 
end function;                                                                                                                                                                                                                          
grant execute on function "dba".sp817_randomdesdehasta(integer,integer) to public as dba;
drop function "dba".sp817_getrandompwd(integer);
create function sp817_getRandomPwd(longitud integer default 15) returning varchar(45)
begin
   define v_pwd varchar(45);
   define v_i integer;
   define v_chrs varchar(100);
   define v_l integer;

   let v_chrs = "POIUYTREWQÑLKJHGFDSAMNBVCXZpoiuytrewqñlkjhgfdsamnbvcxz1234567890";
   let v_l = CHAR_LENGTH(v_chrs);

   if longitud < 8 then
      let longitud = 8;
   elif longitud > 45 then
      let longitud = 45;
   end if;

   let v_pwd = "";
   for v_i in (1 to longitud)
      let v_pwd = v_pwd || SUBSTR(v_chrs, sp817_randomdesdehasta(1,v_l), 1);
   end for;
   
   return v_pwd;

end;
end function;                                                                                                                                       
grant execute on function "dba".sp817_getrandompwd(integer) to public as dba;

Hola Diego, muy bueno el aporte de las funciones en Informix. Un solo comentario, con respecto a POstgres, existen algunas funciones (no tan elaboradas como las que posteaste) que al menos te permiten generar un numero random entre 0 y 1 y luego mediante operaciones lo podes llevar a fechas, rangos, etc.

Las funciones son:
random() dp random value in the range 0.0 <= x < 1.0 random()
setseed(dp) int set seed for subsequent random() calls (value between 0 and 1.0) setseed(0.54823) 1177314959

saludos
Ignacio

Hola Ignacio!

Si tal cual decís, la función Random de PostgreSQL existe y de hecho, funcionan prácticamente idéntico (digo prácticamente por si las dudas;)

Tuve que meterle mano a la función generadora de passwords, ya terminé los cambios así que les comparto la versión 2.0 :smiley:

drop function "dba".sp817_getrandompwd(integer,integer,integer,integer,integer);
create function "dba".sp817_getrandompwd (longitud integer default 15, min_cant_nros integer default 2, min_cant_mayu integer default 1, min_cant_simb integer default 1, min_cant_minu integer default 1) returning varchar(45)
begin
   -- si haciera falta incrementar el tamaño de las variables
   define nros varchar(10);
   define mayu varchar(27);
   define simb varchar(14); 
   define minu varchar(27);
   define chrs varchar(78);

   define i integer;
   define pwd varchar(45);
   define char_dato char;

   -- editar estos strings para personalizar los caracteres de cada grupo
   let nros = "0123456789";
   let minu = "abcdefghijklmnñopqrstuvwxyz";
   let mayu = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ";
   let simb = "@#$%&*?¡¿-_/+.";
   let chrs = nros || minu || mayu || simb;

   -- la longitud de la password generada está acotada (8 a 45) 
   if longitud < 8 then
      let longitud = 8;
   elif longitud > 45 then
      let longitud = 45;
   end if;

   -- aseguramos que la password tenga desde 1 caracter de cada categoría y hasta un máximo de 1/4 de la longitud de la clave. 
   let i = ROUND(longitud/4);
   if min_cant_nros < 1 then
      let min_cant_nros = 1;
   elif min_cant_nros > i then
      let min_cant_nros = i;
   end if;
   if min_cant_mayu < 1 then
      let min_cant_mayu = 1;
   elif min_cant_mayu > i then
      let min_cant_mayu = i;
   end if;
   if min_cant_simb < 1 then
      let min_cant_simb = 1;
   elif min_cant_simb > i then
      let min_cant_simb = i;
   end if;
   if min_cant_minu < 1 then
      let min_cant_minu = 1;
   elif min_cant_minu > longitud-(min_cant_nros+min_cant_mayu+min_cant_simb) then
      let min_cant_minu = longitud-(min_cant_nros+min_cant_mayu+min_cant_simb);
   end if;

   -- tabla auxiliar para simular un array
   create table tmp_pwd_data (orden float, dato char);

   -- números
   for i in (1 to min_cant_nros)
      insert into tmp_pwd_data (orden, dato) values (sp817_random(), SUBSTR(nros, sp817_randomdesdehasta(1,10), 1));
   end for;

   -- símbolos
   for i in (1 to min_cant_simb)
      insert into tmp_pwd_data (orden, dato) values (sp817_random(), SUBSTR(simb, sp817_randomdesdehasta(1,14), 1));
   end for;

   -- mayúsculas
   for i in (1 to min_cant_mayu)
      insert into tmp_pwd_data (orden, dato) values (sp817_random(), SUBSTR(mayu, sp817_randomdesdehasta(1,14), 1));
   end for;

   -- minúsculas
   for i in (1 to min_cant_minu)
      insert into tmp_pwd_data (orden, dato) values (sp817_random(), SUBSTR(minu, sp817_randomdesdehasta(1,27), 1));
   end for;

   -- cualquier cosa
   let i = min_cant_mayu + min_cant_minu + min_cant_simb + min_cant_nros;
   while i < longitud
      insert into tmp_pwd_data (orden, dato) values (sp817_random(), SUBSTR(chrs, sp817_randomdesdehasta(1,78), 1));
      let i = i + 1;
   end while;

   -- arma la password
   let pwd = "";
   foreach select dato into char_dato from tmp_pwd_data order by orden
      let pwd = pwd || char_dato;
   end foreach;

   -- borra la tabla auxiliar
   drop table tmp_pwd_data;

   return pwd;

end;
end function;