Pracuję nad grą, która w pewnym momencie dotyczy pojazdów. Mam tabelę MySQL o nazwie „pojazdy” zawierającą dane o pojazdach, w tym kolumnę „tablica rejestracyjna”, która przechowuje tablice rejestracyjne pojazdów.
Teraz nadchodzi część, z którą mam problemy. Muszę znaleźć nieużywaną tablicę rejestracyjną przed utworzeniem nowego pojazdu - powinien to być alfanumeryczny 8-znakowy losowy ciąg. Jak to osiągnąłem, wykorzystałem pętlę while w Lua, czyli języku, w którym programuję, do generowania ciągów i wysyłania zapytań do bazy danych, aby sprawdzić, czy jest używana. Jednak wraz ze wzrostem liczby pojazdów spodziewam się, że stanie się to jeszcze bardziej nieefektywne, jak jest teraz. Dlatego zdecydowałem się spróbować rozwiązać ten problem za pomocą zapytania MySQL.
Zapytanie, którego potrzebuję, powinno po prostu wygenerować 8-znakowy ciąg alfanumeryczny, którego nie ma jeszcze w tabeli. Ponownie pomyślałem o podejściu do tworzenia i sprawdzania pętli, ale nie ograniczam tego pytania do tego, na wypadek gdyby było bardziej wydajne. Udało mi się wygenerować ciągi znaków, definiując ciąg zawierający wszystkie dozwolone znaki i losowo składający się z niego, i nic więcej.
Każda pomoc jest mile widziana.
Odpowiedzi:
Ten problem składa się z dwóch bardzo różnych podproblemów:
Chociaż przypadkowość jest dość łatwa do osiągnięcia, unikalność bez pętli ponawiania już nie. To prowadzi nas do skoncentrowania się najpierw na wyjątkowości. Nieprzypadkową wyjątkowość można w trywialny sposób osiągnąć za pomocą
AUTO_INCREMENT
. Tak więc użycie pseudolosowej transformacji zachowującej niepowtarzalność byłoby w porządku:RAND(N)
sam!Gwarantujemy, że sekwencja liczb losowych utworzona przez to samo ziarno będzie
INT32
Więc używamy podejścia @ AndreyVolk lub @ GordonLinoff, ale z rozstawionym
RAND
:np. Assumin
id
toAUTO_INCREMENT
kolumna:źródło
RAND(LAST_INSERT_ID()); UPDATE vehicles (...) , rand()*36+1, (...)
(inaczej zwraca 8 razy ten sam znak). Jak możemy być pewni, że 8 kolejnych wywołań funkcjirand()
gwarantuje zwrócenie innej sekwencji, jeśli zostanie zainicjowane innym ziarnem?FLOOR()
wokół drugiego podłańcucha parametry:…
substring('ABC … 789', floor(rand(@seed:= … )*36+1), 1),
…
W niektórych przypadkach podciąg próbował wybrać znak 36.9, który po zaokrągleniu do 37 nie powodowałby wybierania żadnego znaku.floor()
. To sqlfiddle pokazuje, że duplikaty są tworzone dla trzech ciągów znaków.193844
i775771
algorytmu wygeneruje ten sam ciągT82X711
( demo ).Nie zawracałbym sobie głowy prawdopodobieństwem kolizji. Po prostu wygeneruj losowy ciąg i sprawdź, czy istnieje. Jeśli tak, spróbuj ponownie i nie powinieneś robić tego więcej niż kilka razy, chyba że masz już przypisaną ogromną liczbę tablic.
Inne rozwiązanie do generowania 8-znakowego pseudolosowego ciągu w czystym (My) SQL:
Możesz wypróbować następujący (pseudokod):
Ponieważ ten post zyskał nieoczekiwany poziom uwagi, pozwolę sobie podkreślić komentarz ADTC : powyższy fragment kodu jest dość głupi i tworzy kolejne cyfry.
Aby uzyskać nieco mniej głupią przypadkowość, spróbuj zamiast tego czegoś takiego:
Aby uzyskać prawdziwą (kryptograficznie bezpieczną) losowość, użyj
RANDOM_BYTES()
zamiastRAND()
(ale wtedy rozważę przeniesienie tej logiki do warstwy aplikacji).źródło
9
w kodzieSELECT LEFT(UUID(), 9);
,-
na końcu wygenerowanego ciągu zawsze znajduje się dziewiąty znak. To jest stałe. Czemu?SELECT LEFT(REPLACE(UUID(), '-', ''), 16);
A co z obliczeniem wartości skrótu MD5 (lub innego) sekwencyjnych liczb całkowitych, a następnie pobraniem pierwszych 8 znaków.
to znaczy
itp.
Uwaga: nie mam pojęcia, ile można przydzielić przed kolizją (ale byłaby to znana i stała wartość).
edycja: To jest teraz stara odpowiedź, ale widziałem ją ponownie z czasem na moich rękach, więc z obserwacji ...
Szansa na wszystkie liczby = 2,35%
Szansa na wszystkie litery = 0,05%
Pierwsza kolizja, gdy MD5 (82945) = "7b763dcb ..." (taki sam wynik jak MD5 (25302))
źródło
Utwórz losowy ciąg
Oto funkcja MySQL do tworzenia losowego ciągu o określonej długości.
Stosowanie
SELECT RANDSTRING(8)
do zwrócenia ciągu 8 znaków.Możesz dostosować
@allowedChars
.Wyjątkowość nie jest gwarantowana - jak zobaczysz w komentarzach do innych rozwiązań, po prostu nie jest to możliwe. Zamiast tego musisz wygenerować ciąg, sprawdzić, czy jest już używany, i spróbować ponownie, jeśli jest.
Sprawdź, czy losowy ciąg jest już używany
Jeśli chcemy zachować kod sprawdzający kolizje poza aplikacją, możemy stworzyć wyzwalacz:
źródło
Oto jeden sposób, używając cyfr alfanumerycznych jako prawidłowych znaków:
Uwaga: nie ma gwarancji wyjątkowości. Musisz to sprawdzić osobno.
źródło
Oto inna metoda generowania losowego ciągu:
SELECT SUBSTRING(MD5(RAND()) FROM 1 FOR 8) AS myrandomstring
źródło
Możesz użyć funkcji rand () i char () MySQL :
źródło
Możesz wygenerować losowy ciąg alfanumeryczny za pomocą:
Możesz użyć go w
BEFORE INSERT
wyzwalaczu i sprawdzić duplikat w pętli while:Teraz po prostu wstaw swoje dane, takie jak
Wyzwalacz wygeneruje wartość dla
plate
kolumny.( demo sqlfiddle )
Działa to w ten sposób, jeśli kolumna dopuszcza wartości NULL. Jeśli chcesz, aby NIE było NULL, musisz zdefiniować wartość domyślną
Możesz także użyć dowolnego innego algorytmu generowania losowych ciągów w wyzwalaczu, jeśli duże litery alfanumeryczne nie są tym, czego chcesz. Ale wyzwalacz zadba o wyjątkowość.
źródło
pow(36,8)-1
jest liczbową reprezentacjąZZZZZZZZ
. Więc generujemy losową liczbę całkowitą z przedziału od0
„36 ^ 8-1” (od0
do2821109907455
) i konwertujemy ją na ciąg alfanumeryczny między0
iZZZZZZZZ
unsingconv()
. lapad () wypełni ciąg zerami, aż osiągnie długość 8.conv()
obsługuje tylko bazę do 36 (10 cyfr + 26 wielkich liter). Jeśli chcesz uwzględnić małe litery, będziesz potrzebować innego sposobu konwersji liczby na ciąg.Aby wygenerować losowy ciąg, możesz użyć:
SUBSTRING(MD5(RAND()) FROM 1 FOR 8)
Otrzymujesz coś takiego:
353E50CC
źródło
Oto moje rozwiązanie dla łańcucha składającego się z 8 liczb losowych oraz wielkich i małych liter:
Wyjaśnione od wewnątrz:
RAND
generuje liczbę losową od 0 do 1MD5
oblicza sumę MD5 (1), 32 znaki z af i 0-9UNHEX
tłumaczy (2) na 16 bajtów z wartościami od 00 do FFTO_BASE64
koduje (3) jako base64, 22 znaki od az i AZ oraz 0-9 plus „/” i „+”, po których następuje dwa „=”REPLACE
usuwają znaki „/”, „+” i „=” z (4)LEFT
pobiera pierwsze 8 znaków z (5), zmień 8 na coś innego, jeśli potrzebujesz więcej lub mniej znaków w losowym ciąguLPAD
wstawia zera na początku (6), jeśli ma mniej niż 8 znaków; ponownie, w razie potrzeby zmień 8 na coś innegoźródło
I Użyj danych z innej kolumny, aby wygenerować „hash” lub unikalny ciąg
źródło
8 liter alfabetu - wielkie litery:
źródło
Jeśli nie masz identyfikatora lub ziarna, tak jak jest to lista wartości we wstawieniu:
źródło
Proste i wydajne rozwiązanie umożliwiające uzyskanie losowego ciągu 10 znaków z dużymi i małymi literami oraz cyframi:
źródło
Jeśli nie masz nic przeciwko „losowym”, ale całkowicie przewidywalnym tablicom rejestracyjnym, możesz użyć rejestru przesuwnego z liniowym sprzężeniem zwrotnym, aby wybrać następny numer rejestracyjny - gwarantujemy, że przejdziesz przez każdy numer przed powtórzeniem. Jednak bez skomplikowanej matematyki nie będziesz w stanie przejść przez każdy 8-znakowy ciąg alfanumeryczny (otrzymasz 2 ^ 41 z 36 ^ 8 (78%) możliwych tablic). Aby lepiej wypełniało to twoją przestrzeń, możesz wykluczyć literę z tablic (może O), co daje 97%.
źródło
Biorąc pod uwagę całkowitą liczbę potrzebnych postaci, miałbyś bardzo małą szansę na wygenerowanie dwóch dokładnie podobnych tablic rejestracyjnych. W ten sposób prawdopodobnie mógłbyś uciec z generowaniem liczb w LUA.
Masz 36 ^ 8 różnych unikalnych numerów rejestracyjnych (2821,109,907,456, to dużo), nawet gdybyś miał już milion numerów, masz bardzo małą szansę na wygenerowanie takiej, którą już masz, około 0,000035%
Oczywiście wszystko zależy od tego, ile numerów rejestracyjnych ostatecznie utworzysz.
źródło
Ta funkcja generuje losowy ciąg na podstawie długości wejściowej i dozwolonych znaków, takich jak:
kod funkcji:
Ten kod jest oparty na funkcji shuffle string wysyłanej przez „Ross Smith II”
źródło
Aby utworzyć losowy 10-cyfrowy alfanumeryczny , z wyłączeniem podobnych znaków 01oOlI:
Właśnie tego potrzebowałem, aby utworzyć kod kuponu . Mylące znaki są usuwane, aby zmniejszyć liczbę błędów podczas wpisywania ich w formularzu kodu kuponu.
Mam nadzieję, że to komuś pomoże, na podstawie błyskotliwej odpowiedzi Jana Uhliga .
Zobacz odpowiedź Jana, aby dowiedzieć się, jak działa ten kod.
źródło
Użyj tej procedury składowanej i używaj jej za każdym razem, jak
źródło
Prosty sposób na wygenerowanie unikalnego numeru
źródło
Wygeneruj klucz 8-znakowy
źródło
Szukałem czegoś podobnego i postanowiłem stworzyć własną wersję, w której możesz również określić inne ziarno, jeśli chcesz (lista znaków) jako parametr:
Może być używany jako:
Który użyłby wbudowanego materiału siewnego wielkich i małych liter + cyfr. NULL byłoby również wartością zamiast „”.
Ale można określić niestandardowe ziarno podczas wywoływania:
źródło