number - str_random mysql
Generando una cadena aleatoria y Ășnica de 8 caracteres usando MySQL (16)
¿Qué hay de calcular el MD5 (u otro) hash de enteros secuenciales, y luego tomar los primeros 8 caracteres.
es decir
MD5(1) = c4ca4238a0b923820dcc509a6f75849b => c4ca4238
MD5(2) = c81e728d9d4c2f636f067f89cc14862c => c81e728d
MD5(3) = eccbc87e4b5ce2fe28308fd9f2a7baf3 => eccbc87e
etc.
Advertencia: no tengo idea de cuántos podría asignar antes de una colisión (pero sería un valor conocido y constante).
editar: Esta es ahora una respuesta antigua, pero la volví a ver con el tiempo en mis manos, por lo que, a partir de la observación ...
Posibilidad de todos los números = 2.35%
Posibilidad de todas las letras = 0.05%
Primera colisión cuando MD5 (82945) = "7b763dcb ..." (el mismo resultado que MD5 (25302))
Estoy trabajando en un juego que involucra vehículos en algún momento. Tengo una tabla de MySQL llamada "vehículos" que contiene los datos sobre los vehículos, incluida la columna "placa" que almacena las placas de matrícula de los vehículos.
Ahora aquí viene la parte con la que estoy teniendo problemas. Necesito encontrar una matrícula sin usar antes de crear un nuevo vehículo, debería ser una cadena alfanumérica aleatoria de 8 caracteres. Cómo logré esto fue usando un ciclo while en Lua, que es el lenguaje en el que estoy programando, para generar cadenas y consultar el DB para ver si se usa. Sin embargo, a medida que aumenta el número de vehículos, espero que esto se vuelva aún más ineficiente en este momento. Por lo tanto, decidí tratar de resolver este problema utilizando una consulta MySQL.
La consulta que necesito debería simplemente generar una cadena alfanumérica de 8 caracteres que no esté ya en la tabla. Pensé en el enfoque generar y verificar lazo nuevamente, pero no estoy limitando esta pregunta a eso en caso de que haya uno más eficiente. Pude generar cadenas al definir una cadena que contiene todos los caracteres permitidos y subscribirla aleatoriamente, y nada más.
Cualquier ayuda es apreciada.
8 letras del alfabeto - Todas mayúsculas:
UPDATE `tablename` SET `tablename`.`randomstring`= concat(CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25)))CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25))),CHAR(FLOOR(65 + (RAND() * 25))));
Aquí hay otro método para generar una cadena aleatoria:
SELECT SUBSTRING(MD5(RAND()) FROM 1 FOR 8) AS myrandomstring
Aquí hay una forma, usando alfanuméricos como caracteres válidos:
select concat(substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand()*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand()*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand()*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand()*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand()*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand()*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand()*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand()*36+1, 1)
) as LicensePlaceNumber;
Tenga en cuenta que no hay garantía de exclusividad. Tendrás que verificar eso por separado.
Como dije en mi comentario, no me molestaría con la probabilidad de colisión. Simplemente genere una cadena aleatoria y verifique si existe. Si lo hace, inténtelo de nuevo y no debería necesitar hacerlo más que un par de veces a menos que tenga una gran cantidad de placas ya asignadas.
Otra solución para generar una cadena pseudoaleatoria larga de 8 caracteres en pure (My) SQL:
SELECT LEFT(UUID(), 8);
Puedes probar lo siguiente (pseudo-código):
DO
SELECT LEFT(UUID(), 8) INTO @plate;
INSERT INTO plates (@plate);
WHILE there_is_a_unique_constraint_violation
-- @plate is your newly assigned plate number
Este problema consiste en dos sub-problemas muy diferentes:
- la cadena debe ser aparentemente aleatoria
- la cadena debe ser única
Si bien la aleatoriedad se logra con bastante facilidad, la singularidad sin un ciclo de reintento no lo es. Esto nos lleva a concentrarnos en la singularidad primero. La singularidad no aleatoria se puede lograr trivialmente con AUTO_INCREMENT
. Por lo tanto, usar una transformación pseudoaleatoria que preserve la singularidad estaría bien:
- Hash ha sido sugerido por @paul
- AES-encrypt también se adapta
- Pero hay uno bueno: ¡
RAND(N)
sí mismo!
Se garantiza que una secuencia de números aleatorios creada por la misma semilla será
- reproducible
- diferente para las primeras 8 iteraciones
- si la semilla es un INT32
Entonces usamos el enfoque de @AreyreyVolk o @ GordonLinoff, pero con un RAND
semilla :
por ejemplo, Assumin id
es una columna AUTO_INCREMENT:
INSERT INTO vehicles VALUES (blah); -- leaving out the number plate
SELECT @lid:=LAST_INSERT_ID();
UPDATE vehicles SET numberplate=concat(
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand(@seed:=round(rand(@lid)*4294967296))*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand(@seed:=round(rand(@seed)*4294967296))*36+1, 1),
substring(''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'', rand(@seed)*36+1, 1)
)
WHERE id=@lid;
Para generar cadena aleatoria, puede usar:
SUBSTRING(MD5(RAND()) FROM 1 FOR 8)
Usted recibe algo así:
353E50CC
Para una cadena que consta de 8 números aleatorios y letras mayúsculas y minúsculas, esta es mi solución:
LPAD(LEFT(REPLACE(REPLACE(REPLACE(TO_BASE64(UNHEX(MD5(RAND()))), "/", ""), "+", ""), "=", ""), 8), 8, 0)
Explicado desde adentro hacia afuera
-
RAND
genera un número aleatorio entre 0 y 1 -
MD5
calcula la suma de MD5 de (1), 32 caracteres de af y 0-9 -
UNHEX
traduce (2) en 16 bytes con valores de 00 a FF -
TO_BASE64
codifica (3) como base64, 22 caracteres de az y AZ y 0-9 más "/" y "+", seguidos por dos "=" - los tres
REPLACE
s eliminan los caracteres "/", "+" y "=" de (4) -
LEFT
toma los primeros 8 caracteres de (5), cambia 8 a otra cosa si necesitas más o menos caracteres en tu cadena aleatoria -
LPAD
inserta ceros al comienzo de (6) si tiene menos de 8 caracteres de longitud; de nuevo, cambie 8 a otra cosa si es necesario
Puede generar una cadena alfanumérica aleatoria con:
lpad(conv(floor(rand()*pow(36,8)), 10, 36), 8, 0);
Puedes usarlo en un disparador BEFORE INSERT
y verificar si hay un duplicado en un ciclo while:
CREATE TABLE `vehicles` (
`plate` CHAR(8) NULL DEFAULT NULL,
`data` VARCHAR(50) NOT NULL,
UNIQUE INDEX `plate` (`plate`)
);
DELIMITER //
CREATE TRIGGER `vehicles_before_insert` BEFORE INSERT ON `vehicles`
FOR EACH ROW BEGIN
declare str_len int default 8;
declare ready int default 0;
declare rnd_str text;
while not ready do
set rnd_str := lpad(conv(floor(rand()*pow(36,str_len)), 10, 36), str_len, 0);
if not exists (select * from vehicles where plate = rnd_str) then
set new.plate = rnd_str;
set ready := 1;
end if;
end while;
END//
DELIMITER ;
Ahora solo inserta tus datos como
insert into vehicles(col1, col2) values (''value1'', ''value2'');
Y el gatillo generará un valor para la columna de la plate
.
Eso funciona de esta manera si la columna permite valores NULL. Si desea que NO sea NULO, deberá definir un valor predeterminado
`plate` CHAR(8) NOT NULL DEFAULT ''default'',
También puede usar cualquier otro algoritmo de generación de cadenas aleatorias en el desencadenador si los caracteres alfanuméricos en mayúsculas no son lo que desea. Pero el disparador se ocupará de la singularidad.
Si está de acuerdo con placas de matrícula "aleatorias" pero completamente predecibles, puede usar un registro de desplazamiento de retroalimentación lineal para elegir el número de placa siguiente - se garantiza que pasará por todos los números antes de repetir. Sin embargo, sin algunas matemáticas complejas, no podrá pasar por cada cadena alfanumérica de 8 caracteres (obtendrá 2 ^ 41 de las 368 placas posibles (78%)). Para que esto llene mejor tu espacio, puedes excluir una carta de las placas (tal vez O), lo que te da un 97%.
Si no tiene un id o seed, como es para una lista de valores en insert:
REPLACE(RAND(), ''.'', '''')
Teniendo en cuenta la cantidad total de caracteres que necesita, tendría muy pocas posibilidades de generar dos matrículas exactamente iguales. Por lo tanto, probablemente podría salirse con la suya generando los números en LUA.
Tiene 36 ^ 8 matrículas únicas diferentes (2,821,109,907,456, eso es mucho), incluso si ya tiene un millón de matrículas, tendría muy pocas posibilidades de generar una que ya tenga, aproximadamente 0,000035%
Por supuesto, todo depende de la cantidad de matrículas que terminará creando.
Uso datos de otra columna para generar un "hash" o una cadena única
UPDATE table_name SET column_name = Right( MD5(another_column_with_data), 8 )
Crea una cadena al azar
Aquí hay una función de MySQL para crear una cadena aleatoria de una longitud determinada.
DELIMITER $$
CREATE DEFINER=`root`@`%` FUNCTION `RandString`(length SMALLINT(3)) RETURNS varchar(100) CHARSET utf8
begin
SET @returnStr = '''';
SET @allowedChars = ''ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'';
SET @i = 0;
WHILE (@i < length) DO
SET @returnStr = CONCAT(@returnStr, substring(@allowedChars, FLOOR(RAND() * LENGTH(@allowedChars) + 1), 1));
SET @i = @i + 1;
END WHILE;
RETURN @returnStr;
END
Uso SELECT RANDSTRING(8)
para devolver una cadena de 8 caracteres.
Puede personalizar los @allowedChars
.
La exclusividad no está garantizada; como verá en los comentarios a otras soluciones, esto simplemente no es posible. En su lugar, deberá generar una cadena, verificar si ya está en uso e intentarlo nuevamente si es así.
Compruebe si la cadena aleatoria ya está en uso
Si queremos mantener el código de verificación de colisión fuera de la aplicación, podemos crear un disparador:
DELIMITER $$
CREATE TRIGGER Vehicle_beforeInsert
BEFORE INSERT ON `Vehicle`
FOR EACH ROW
BEGIN
SET @vehicleId = 1;
WHILE (@vehicleId IS NOT NULL) DO
SET NEW.plate = RANDSTRING(8);
SET @vehicleId = (SELECT id FROM `Vehicle` WHERE `plate` = NEW.plate);
END WHILE;
END;$$
DELIMITER ;
DELIMITER $$
USE `temp` $$
DROP PROCEDURE IF EXISTS `GenerateUniqueValue`$$
CREATE PROCEDURE `GenerateUniqueValue`(IN tableName VARCHAR(255),IN columnName VARCHAR(255))
BEGIN
DECLARE uniqueValue VARCHAR(8) DEFAULT "";
WHILE LENGTH(uniqueValue) = 0 DO
SELECT CONCAT(SUBSTRING(''ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'', RAND()*34+1, 1),
SUBSTRING(''ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'', RAND()*34+1, 1),
SUBSTRING(''ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'', RAND()*34+1, 1),
SUBSTRING(''ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'', RAND()*34+1, 1),
SUBSTRING(''ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'', RAND()*34+1, 1),
SUBSTRING(''ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'', RAND()*34+1, 1),
SUBSTRING(''ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'', RAND()*34+1, 1),
SUBSTRING(''ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'', RAND()*34+1, 1)
) INTO @newUniqueValue;
SET @rcount = -1;
SET @query=CONCAT(''SELECT COUNT(*) INTO @rcount FROM '',tableName,'' WHERE '',columnName,'' like '''''',@newUniqueValue,'''''''');
PREPARE stmt FROM @query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
IF @rcount = 0 THEN
SET uniqueValue = @newUniqueValue ;
END IF ;
END WHILE ;
SELECT uniqueValue;
END$$
DELIMITER ;
Use este procedimiento almacenado y úselo cada vez que lo desee
Call GenerateUniqueValue(''tableName'',''columnName'')