나는 어느 시점에서 차량과 관련된 게임을 작업하고 있습니다. 차량에 대한 번호판을 저장하는 “plate”열을 포함하여 차량에 대한 데이터를 포함하는 “vehicles”라는 MySQL 테이블이 있습니다.
이제 제가 문제가있는 부분이 있습니다. 새 차량을 만들기 전에 사용하지 않은 번호판을 찾아야합니다. 영숫자 8 자로 된 임의의 문자열이어야합니다. 내가 이것을 달성 한 방법은 내가 프로그래밍하는 언어 인 Lua에서 while 루프를 사용하여 문자열을 생성하고 DB를 쿼리하여 사용 여부를 확인하는 것이 었습니다. 하지만 차량 수가 늘어남에 따라 지금보다 훨씬 비효율적 일 것으로 예상됩니다. 따라서 MySQL 쿼리를 사용하여이 문제를 해결하기로 결정했습니다.
필요한 쿼리는 테이블에 아직없는 8 자 영숫자 문자열을 생성해야합니다. 다시 생성 및 확인 루프 접근 방식을 생각했지만 더 효율적인 질문이있는 경우에만이 질문을 제한하지 않습니다. 허용 된 모든 문자를 포함하는 문자열을 정의하고 임의로 하위 문자열을 지정하여 문자열을 생성 할 수있었습니다.
도움을 주시면 감사하겠습니다.
답변
이 문제는 매우 다른 두 가지 하위 문제로 구성됩니다.
- 문자열은 겉보기에는 무작위로 보여야합니다.
- 문자열은 고유해야합니다.
임의성은 매우 쉽게 얻을 수 있지만 재시도 루프가없는 고유성은 그렇지 않습니다. 이것은 우리가 먼저 독창성에 집중하게합니다. 무작위가 아닌 고유성은 AUTO_INCREMENT
. 따라서 고유성을 유지하는 의사 랜덤 변환을 사용하는 것이 좋습니다.
- @paul이 해시를 제안했습니다.
- AES 암호화도 적합합니다.
- 그러나 좋은 것이 있습니다 :
RAND(N)
그 자체입니다!
동일한 시드로 생성 된 일련의 난수는
- 재생할 수 있는
- 처음 8 회 반복에 대해 다름
- 씨앗이
INT32
그래서 우리는 @AndreyVolk 또는 @GordonLinoff의 접근 방식을 사용하지만 시드가 있습니다 RAND
.
예를 들어 Assumin id
은 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;
답변
내 의견에서 언급했듯이 충돌 가능성에 대해서는 신경 쓰지 않습니다. 임의의 문자열을 생성하고 존재하는지 확인하십시오. 만약 그렇다면, 다시 시도하세요. 이미 많은 수의 플레이트가 할당되어 있지 않는 한 두 번 더 할 필요가 없습니다.
순수 (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