it-swarm.com.de

Generieren einer zufälligen und eindeutigen 8-stelligen Zeichenfolge mit MySQL

Ich arbeite an einem Spiel, an dem irgendwann Fahrzeuge beteiligt sind. Ich habe eine MySQL-Tabelle mit dem Namen "Fahrzeuge", die die Daten zu den Fahrzeugen enthält, einschließlich der Spalte "Platte", in der die Nummernschilder für die Fahrzeuge gespeichert sind.

Jetzt kommt der Teil, mit dem ich Probleme habe. Ich muss ein unbenutztes Nummernschild finden, bevor Sie ein neues Fahrzeug erstellen - es sollte eine alphanumerische zufällige Zeichenfolge mit 8 Zeichen sein. Wie ich dies erreicht habe, war die Verwendung einer while-Schleife in Lua, der Sprache, in der ich programmiere, um Strings zu generieren und die Datenbank abzufragen, ob sie verwendet wird. Da die Anzahl der Fahrzeuge jedoch zunimmt, erwarte ich, dass dies noch ineffizienter wird, jetzt. Daher entschied ich mich, dieses Problem mit einer MySQL-Abfrage zu lösen.

Die Abfrage, die ich brauche, sollte einfach eine 8-stellige alphanumerische Zeichenfolge generieren, die noch nicht in der Tabelle enthalten ist. Ich habe noch einmal an den Generate & Check Loop-Ansatz gedacht, aber ich beschränke mich nicht auf diese Frage, nur für den Fall, dass es eine effizientere gibt. Ich konnte Strings generieren, indem ich einen String definierte, der alle zulässigen Zeichen enthielt und zufällig einen Teil des Strings enthielt, und nichts weiter. 

Jede Hilfe wird geschätzt.

73
funstein

Dieses Problem besteht aus zwei sehr unterschiedlichen Unterproblemen:

  • die Zeichenfolge muss scheinbar zufällig sein
  • die Zeichenfolge muss eindeutig sein

Die Zufälligkeit ist zwar leicht zu erreichen, die Einzigartigkeit ohne Wiederholungsschleife jedoch nicht. Dies führt uns dazu, uns zuerst auf die Einzigartigkeit zu konzentrieren. Nicht-zufällige Eindeutigkeit kann mit AUTO_INCREMENT leicht erreicht werden. Die Verwendung einer Pseudo-Zufalls-Transformation, die die Eindeutigkeit bewahrt, wäre also in Ordnung:

  • Hash wurde von @paul vorgeschlagen
  • AES-Verschlüsselung passt auch
  • Aber es gibt ein schönes: Rand(N) selbst!

Eine Folge von Zufallszahlen, die von demselben Samen erstellt werden, ist garantiert 

  • reproduzierbar 
  • anders für die ersten 8 Iterationen
  • wenn der Samen ein INT32 ist

Also verwenden wir @ AndreyVolks oder @ GordonLinoffs Ansatz, jedoch mit einem seeded Rand:

z.B. Assumin id ist eine AUTO_INCREMENT-Spalte: 

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 [email protected];
69
Eugen Rieck

Wie ich in meinem Kommentar feststellte, habe ich mich nicht mit der Wahrscheinlichkeit einer Kollision beschäftigt. Erzeugen Sie einfach eine zufällige Zeichenfolge und prüfen Sie, ob sie existiert. Wenn dies der Fall ist, versuchen Sie es erneut und Sie sollten es nicht mehr als ein paar Mal tun, es sei denn, Sie haben bereits eine große Anzahl von Platten zugewiesen.

Eine weitere Lösung zum Generieren einer 8-stelligen Pseudo-Zufallszeichenfolge in Pure (My) SQL:

SELECT LEFT(UUID(), 8);

Sie können folgendes versuchen (Pseudo-Code):

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
73
RandomSeed

Wie wäre es mit der Berechnung des MD5-Hashes (oder eines anderen) von sequentiellen Ganzzahlen, und nehmen Sie dann die ersten 8 Zeichen.

d.h. 

MD5(1) = c4ca4238a0b923820dcc509a6f75849b => c4ca4238
MD5(2) = c81e728d9d4c2f636f067f89cc14862c => c81e728d
MD5(3) = eccbc87e4b5ce2fe28308fd9f2a7baf3 => eccbc87e

usw.

vorbehalt: Ich habe keine Ahnung, wie viele Sie vor einer Kollision zuweisen könnten (aber es wäre ein bekannter und konstanter Wert).

edit: Das ist jetzt eine alte Antwort, aber ich habe es mit der Zeit wieder gesehen, also aus Beobachtung ...

Chance auf alle Zahlen = 2,35%

Chance auf alle Buchstaben = 0,05%

Erste Kollision bei MD5 (82945) = "7b763dcb ..." (gleiches Ergebnis wie MD5 (25302))

48
paul

Erstellen Sie eine zufällige Zeichenfolge

Hier ist eine MySQL-Funktion zum Erstellen einer zufälligen Zeichenfolge mit einer bestimmten Länge.

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

Verwenden Sie SELECT RANDSTRING(8), um eine Zeichenfolge mit 8 Zeichen zurückzugeben.

Sie können den @allowedChars anpassen.

Einzigartigkeit ist nicht garantiert - wie Sie in den Kommentaren zu anderen Lösungen sehen werden, ist dies einfach nicht möglich. Stattdessen müssen Sie eine Zeichenfolge generieren, prüfen, ob sie bereits verwendet wird, und es erneut versuchen.


Überprüfe, ob die zufällige Zeichenfolge bereits verwendet wird

Wenn Sie den Code für die Kollisionsprüfung aus der App heraushalten möchten, können Sie einen Auslöser erstellen:

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 ;
24
Paddy Mann

Hier ist eine andere Methode zum Generieren einer zufälligen Zeichenfolge:

SELECT SUBSTRING(MD5(Rand()) FROM 1 FOR 8) AS myrandomstring

21
beingalex

Hier ist eine Möglichkeit, alphanumerische Zeichen als gültige Zeichen zu verwenden:

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;

Hinweis: Es gibt keine Garantie für die Einzigartigkeit. Sie müssen dies gesondert prüfen.

21
Gordon Linoff

Sie können die Funktion Rand () und char () von MySQL verwenden:

select concat( 
    char(round(Rand()*25)+97),
    char(round(Rand()*25)+97),
    char(round(Rand()*25)+97),
    char(round(Rand()*25)+97),
    char(round(Rand()*25)+97),
    char(round(Rand()*25)+97),
    char(round(Rand()*25)+97),
    char(round(Rand()*25)+97)
) as name;
13
Andrey Volk

Sie können eine zufällige alphanumerische Zeichenfolge erstellen mit:

lpad(conv(floor(Rand()*pow(36,8)), 10, 36), 8, 0);

Sie können es in einem BEFORE INSERT-Trigger verwenden und in einer while-Schleife nach einem Duplikat suchen:

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 ;

Jetzt einfach deine Daten einfügen

insert into vehicles(col1, col2) values ('value1', 'value2');

Der Trigger generiert einen Wert für die Spalte plate.

( sqlfiddle Demo )

Das funktioniert so, wenn die Spalte NULL zulässt. Wenn Sie NOT NULL haben möchten, müssen Sie einen Standardwert definieren

`plate` CHAR(8) NOT NULL DEFAULT 'default',

Sie können auch einen beliebigen anderen Algorithmus für die Generierung von Zufallsstrings im Trigger verwenden, wenn die Großbuchstaben nicht ausreichen. Der Auslöser sorgt jedoch für die Einzigartigkeit.

11
Paul Spiegel

Zum Generieren einer zufälligen Zeichenfolge können Sie Folgendes verwenden:

SUBSTRING(MD5(Rand()) FROM 1 FOR 8)

Sie erhalten so etwas:

353E50CC

6
Nikita G.

I Verwenden Sie Daten aus einer anderen Spalte, um einen "Hash" oder eine eindeutige Zeichenfolge zu generieren

UPDATE table_name SET column_name = Right( MD5(another_column_with_data), 8 )
3

8 Buchstaben aus dem Alphabet - Alle Buchstaben:

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))));
2
TV-C-15

Wenn Sie keine ID oder einen Seed haben, wie es für eine Werteliste in insert gilt

REPLACE(Rand(), '.', '')
2
ekerner

Für einen String, der aus 8 Zufallszahlen und Groß- und Kleinbuchstaben besteht, ist dies meine Lösung:

LPAD(LEFT(REPLACE(REPLACE(REPLACE(TO_BASE64(UNHEX(MD5(Rand()))), "/", ""), "+", ""), "=", ""), 8), 8, 0)

Von innen heraus erklärt:

  1. Rand generiert eine Zufallszahl zwischen 0 und 1
  2. MD5 berechnet die MD5-Summe von (1), 32 Zeichen aus a-f und 0-9
  3. UNHEX übersetzt (2) in 16 Bytes mit Werten von 00 bis FF
  4. TO_BASE64 kodiert (3) als base64, 22 Zeichen von a-z und A-Z und 0-9 plus "/" und "+", gefolgt von zwei "="
  5. die drei REPLACEs entfernen die Zeichen "/", "+" und "=" aus (4)
  6. LEFT übernimmt die ersten 8 Zeichen von (5), ändern Sie 8 in etwas anderes, wenn Sie mehr oder weniger Zeichen in Ihrer zufälligen Zeichenfolge benötigen
  7. LPAD fügt am Anfang von (6) Nullen ein, wenn sie weniger als 8 Zeichen lang ist; Ändern Sie 8 erneut in etwas anderes, falls erforderlich
1
Jan Uhlig

Wenn Sie mit "zufälligen", aber völlig vorhersehbaren Kennzeichen zufrieden sind, können Sie ein Linear-Feedback-Schieberegister verwenden, um die nächste Kennzeichennummer zu wählen. Es wird garantiert, dass alle Nummern vor dem Wiederholen durchlaufen werden. Ohne komplexe Berechnungen können Sie jedoch nicht alle 8-stelligen alphanumerischen Zeichenfolgen durchgehen (Sie erhalten 2 ^ 41 von 36 ^ 8 (78%) möglichen Platten). Um dies besser auszufüllen, können Sie einen Buchstaben von den Platten ausschließen (möglicherweise 0), was 97% ergibt.

1
τεκ

Diese Funktion generiert eine zufällige Zeichenfolge basierend auf Ihrer Eingabelänge und folgenden zulässigen Zeichen:

SELECT str_Rand(8, '23456789abcdefghijkmnpqrstuvwxyz');

funktionscode:

DROP FUNCTION IF EXISTS str_Rand;

DELIMITER //

CREATE FUNCTION str_Rand(
    u_count INT UNSIGNED,
    v_chars TEXT
)
RETURNS TEXT
NOT DETERMINISTIC
NO SQL
SQL SECURITY INVOKER
COMMENT ''
BEGIN
    DECLARE v_retval TEXT DEFAULT '';
    DECLARE u_pos    INT UNSIGNED;
    DECLARE u        INT UNSIGNED;

    SET u = LENGTH(v_chars);
    WHILE u_count > 0
    DO
      SET u_pos = 1 + FLOOR(Rand() * u);
      SET v_retval = CONCAT(v_retval, MID(v_chars, u_pos, 1));
      SET u_count = u_count - 1;
    END WHILE;

    RETURN v_retval;
END;
//
DELIMITER ;

Dieser Code basiert auf Shuffle-String-Funktion und wird von "Ross Smith II" gesendet.

1
Mahoor13

Wenn man die Gesamtzahl der Zeichen berücksichtigt, die Sie benötigen, haben Sie eine sehr geringe Chance, genau zwei gleiche Nummernschilder zu erzeugen. So könnten Sie wahrscheinlich mit der Erzeugung der Zahlen in LUA davonkommen.

Sie haben 36 ^ 8 verschiedene eindeutige Nummernschilder (2.821.109.907.456, das ist eine Menge). Selbst wenn Sie bereits über eine Million Nummernschilder verfügen, haben Sie eine sehr geringe Chance, eines zu erzeugen, das Sie bereits haben.

Natürlich hängt alles davon ab, wie viele Nummernschilder Sie erstellen werden.

1

Ein einfacher Weg, um eine eindeutige Nummer zu generieren 

set @i = 0;
update vehicles set plate = CONCAT(@i:[email protected]+1, ROUND(Rand() * 1000)) 
order by Rand();
0
Gautier
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 ;

Verwenden Sie diese gespeicherte Prozedur und verwenden Sie sie immer gerne

Call GenerateUniqueValue('tableName','columnName')

Generieren Sie einen 8-stelligen Schlüssel

lpad(conv(floor(Rand()*pow(36,6)), 10, 36), 8, 0); 

Wie erstelle ich eine eindeutige zufällige Zeichenfolge für eine meiner MySql-Tabellenspalten?

0