[mysql] MySQL을 사용하여 임의의 고유 한 8 자 문자열 생성

나는 어느 시점에서 차량과 관련된 게임을 작업하고 있습니다. 차량에 대한 번호판을 저장하는 “plate”열을 포함하여 차량에 대한 데이터를 포함하는 “vehicles”라는 MySQL 테이블이 있습니다.

이제 제가 문제가있는 부분이 있습니다. 새 차량을 만들기 전에 사용하지 않은 번호판을 찾아야합니다. 영숫자 8 자로 된 임의의 문자열이어야합니다. 내가 이것을 달성 한 방법은 내가 프로그래밍하는 언어 인 Lua에서 while 루프를 사용하여 문자열을 생성하고 DB를 쿼리하여 사용 여부를 확인하는 것이 었습니다. 하지만 차량 수가 늘어남에 따라 지금보다 훨씬 비효율적 일 것으로 예상됩니다. 따라서 MySQL 쿼리를 사용하여이 문제를 해결하기로 결정했습니다.

필요한 쿼리는 테이블에 아직없는 8 자 영숫자 문자열을 생성해야합니다. 다시 생성 및 확인 루프 접근 방식을 생각했지만 더 효율적인 질문이있는 경우에만이 질문을 제한하지 않습니다. 허용 된 모든 문자를 포함하는 문자열을 정의하고 임의로 하위 문자열을 지정하여 문자열을 생성 할 수있었습니다.

도움을 주시면 감사하겠습니다.



답변

이 문제는 매우 다른 두 가지 하위 문제로 구성됩니다.

  • 문자열은 겉보기에는 무작위로 보여야합니다.
  • 문자열은 고유해야합니다.

임의성은 매우 쉽게 얻을 수 있지만 재시도 루프가없는 고유성은 그렇지 않습니다. 이것은 우리가 먼저 독창성에 집중하게합니다. 무작위가 아닌 고유성은 AUTO_INCREMENT. 따라서 고유성을 유지하는 의사 랜덤 변환을 사용하는 것이 좋습니다.

  • @paul이 해시를 제안했습니다.
  • AES 암호화도 적합합니다.
  • 그러나 좋은 것이 있습니다 : RAND(N)그 자체입니다!

동일한 시드로 생성 된 일련의 난수는

  • 재생할 수 있는
  • 처음 8 회 반복에 대해 다름
  • 씨앗이 INT32

그래서 우리는 @AndreyVolk 또는 @GordonLinoff의 접근 방식을 사용하지만 시드가 있습니다 RAND .

예를 들어 Assumin idAUTO_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;


답변

내 의견에서 언급했듯이 충돌 가능성에 대해서는 신경 쓰지 않습니다. 임의의 문자열을 생성하고 존재하는지 확인하십시오. 만약 그렇다면, 다시 시도하세요. 이미 많은 수의 플레이트가 할당되어 있지 않는 한 두 번 더 할 필요가 없습니다.

순수 (My) SQL에서 8 자 길이의 의사 랜덤 문자열을 생성하는 또 다른 솔루션 :

SELECT LEFT(UUID(), 8);

다음 (의사 코드)을 시도 할 수 있습니다.

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

이 게시물이 예상치 못한 수준의 관심을 받았으므로 ADTC의 의견을 강조 하겠습니다 . 위의 코드는 매우 멍청하고 연속적인 숫자를 생성합니다.

약간 덜 어리석은 무작위성을 위해 대신 다음과 같이 시도하십시오.

SELECT LEFT(MD5(RAND()), 8)

그리고 진정한 (암호 화학적으로 안전한) 임의성을 위해 RANDOM_BYTES()대신 사용하십시오 RAND()(하지만이 논리를 애플리케이션 계층으로 이동하는 것을 고려할 것입니다).


답변

순차 정수의 MD5 (또는 기타) 해시를 계산 한 다음 처음 8자를 사용하는 것은 어떻습니까?

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

기타

주의 : 충돌 전에 얼마나 많은 것을 할당 할 수 있는지 모르겠습니다 (하지만 알고 있고 일정한 값이 될 것입니다).

편집 : 이것은 이제 오래된 대답이지만 시간이 지남에 따라 다시 보았으므로 관찰에서 …

모든 숫자의 확률 = 2.35 %

모든 글자의 확률 = 0.05 %

MD5 (82945) = “7b763dcb …”일 때 첫 번째 충돌 (MD5 (25302)과 동일한 결과)


답변

임의의 문자열 만들기

다음은 주어진 길이의 임의의 문자열을 생성하는 MySQL 함수입니다.

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

SELECT RANDSTRING(8)8 자 문자열을 반환하는 데 사용 됩니다.

당신은 사용자 정의 할 수 있습니다 @allowedChars.

고유성이 보장되지는 않습니다. 다른 솔루션에 대한 의견에서 볼 수 있듯이 이것은 불가능합니다. 대신 문자열을 생성하고 이미 사용 중인지 확인한 다음 사용중인 경우 다시 시도해야합니다.


임의의 문자열이 이미 사용 중인지 확인하십시오.

앱에서 충돌 검사 코드를 유지하려면 트리거를 만들 수 있습니다.

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 ;


답변

다음은 영숫자를 유효한 문자로 사용하는 한 가지 방법입니다.

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;

고유성을 보장하지 않습니다. 별도로 확인해야합니다.


답변

다음은 임의의 문자열을 생성하는 또 다른 방법입니다.

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


답변

MySQL의 rand ()char () 함수를 사용할 수 있습니다 .

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;