Recientemente me enfrenté al desafío de crear nombres de usuarios únicos de forma automática. Me propuse resolver el caso específico de generar un username único concatenando el primer nombre y apellido (first and last name), pero con la flexibilidad de que la solución fuera reutilizable para otros tipos de entidades.
Ejemplo del problema:
– Usuario 1: john.doe
– Usuario 2: john.doe → ¿Qué hacer?
La Solución
Se desarrolló una solución genérica que no solo resuelve el problema de usernames únicos, sino que puede aplicarse a cualquier entidad que requiera nombres únicos (proyectos, tenants, etc.).
Los recursos de esta solución los podemos encontrar en:
– core_users.sql
– core_users_bi_trg.sql
– core_unique_names.sql
– core_unique_name_manager.pks
– core_unique_name_manager.pkb

Componentes Principales
1. Tabla Genérica: core_unique_names
create table core_unique_names (
name_id number generated by default on null as identity (start with 1) primary key not null
, entity_type varchar2(50 char) not null
, base_name varchar2(100 char) not null
, counter number default 0 not null
, last_used timestamp with local time zone default localtimestamp not null);
2. Package Genérico: core_unique_name_manager
create or replace package core_unique_name_manager as
-- Función genérica para cualquier tipo de entidad
function generate_unique_name(
p_entity_type in varchar2
, p_base_name in varchar2
) return varchar2;
-- Wrapper específico para usernames
function generate_unique_username(
p_first_name in varchar2
, p_last_name in varchar2
) return varchar2;
end;
function generate_unique_name(
p_entity_type in varchar2
, p_base_name in varchar2
) return varchar2
is
l_counter number;
l_unique_name varchar2(100);
l_clean_name varchar2(100);
begin
-- Clean and validate base name
l_clean_name := clean_name(p_base_name);
if l_clean_name is null then
raise_application_error(-20001, 'Base name cannot be null or empty');
end if;
if length(l_clean_name) < 2 then
raise_application_error(-20002, 'Generated name is too short');
end if;
-- Get or create counter for this base name
begin
select counter + 1
into l_counter
from core_unique_names
where entity_type = p_entity_type
and base_name = l_clean_name
and active_yn = 'Y'
for update;
-- Update counter
update core_unique_names
set counter = l_counter
, last_used = localtimestamp
where entity_type = p_entity_type
and base_name = l_clean_name
and active_yn = 'Y';
exception
when no_data_found then
-- First occurrence, create counter record
l_counter := 0;
insert into core_unique_names (
entity_type
, base_name
, counter
) values (
p_entity_type
, l_clean_name
, 1
);
end;
-- Generate unique name
if l_counter = 0 then
l_unique_name := l_clean_name;
else
l_unique_name := l_clean_name || '_' || l_counter;
end if;
return l_unique_name;
end generate_unique_name;
Cómo Funciona
Paso 1: Generación del Nombre Base
Para usernames, convertimos el nombre completo a un formato estándar:
– Juan Carlos Pérez → juan_carlos.perez
– María José García-López → maria_jose.garcia-lopez
Paso 2: Verificación de Unicidad
El sistema busca en la tabla core_unique_names si ya existe un registro para ese nombre base.
Paso 3: Generación del Nombre Único
– Primera vez: juan_carlos.perez
– Segunda vez: juan_carlos.perez_1
– Tercera vez: juan_carlos.perez_2
Implementación Automática
Trigger en la Tabla de Usuarios
create or replace trigger core_users_bi_trg
before insert
on core_users
referencing old as old new as new
for each row
begin
-- Generar username único automáticamente
:new.username := core_unique_name_manager.generate_unique_username(
:new.first_name
, :new.last_name
);
end;
/
Ventajas de esta Solución
1. Genérica: No solo para usernames, sino para cualquier entidad
2. Automática: No requiere intervención manual
3. Consistente: Mismo formato para todos los usuarios
4. Escalable: Maneja miles de usuarios sin problemas
5. Auditable: Mantiene historial de nombres utilizados
Aplicación a Otros Tipos de Entidades
Esta solución puede extenderse fácilmente a otros tipos de entidades:
-- Para proyectos
project_name := core_unique_name_manager.generate_unique_name(
'PROJECT'
, 'Mi Proyecto'
);
-- Resultado: 'mi_proyecto', 'mi_proyecto_1', etc.
-- Para tenants
tenant_name := core_unique_name_manager.generate_unique_name(
'TENANT'
, 'Empresa ABC'
);
-- Resultado: 'empresa_abc', 'empresa_abc_1', etc.