taral
@taral
php программист

Генерация уникального ID

На сайте постоянно создаются заказы. На каждый заказ необходимо сгенерировать уникальный id. Он должен быть достаточно простым для восприятия человеком и обязательно не должен повторятся.
Нельзя использовать id с базы данных и как либо на него ориентироваться.
Пока придумал подвязаться на time() и частично перевести его в цифры. Для лучшей читаемости.
Примерно так
код PHP
$letters='QWERTYUIOPASDFGHJKLZXCVBNM1234567890';
$count=strlen($letters);
$intval=time();
$result='';
for($i=0;$i<4;$i++) {
        $last=$intval%$count;
        $intval=($intval-$last)/$count;
        $result.=$letters[$last];
}
echo $result.$intval;


в результате получаю примерно такой код WKI0-811
в конец добавляю rand(10,99) для исключения дубляжей в ту же секунду. Получаю такое WKI0-811-45
Длинна и сложность кода устраивает. Не нравится 1. Возможность дубляжей хоть и очень небольшая. 2. Окончание 811 очень редко изменяется. Поскольку это грубо говоря года из time().
Также я уверен что есть более красивые решения. Спасибо за внимание
  • Вопрос задан
  • 50442 просмотра
Пригласить эксперта
Ответы на вопрос 16
B7W
@B7W
Есть стандарт UUID. На его основе сделайте свой.
Ответ написан
Комментировать
BoShurik
@BoShurik
Symfony developer
Я использую это:
base_convert(sha1(uniqid(mt_rand(), true)), 16, 36);
Ответ написан
alexvy
@alexvy
интернет-маркетолог
самый простой способ - взять unix-время, разбить на группы разрядов:
140-785-156
уникальности с девятью разрядами хватит на 30 лет
даже если конкурент догадается как вы получаете ID - это ему ничем не поможет

UPD: Проблему уникальности можно решить модифиицируя непосрдственно ключевое поле таблицы заказов - ALTER TABLE table_order AUTO_INCREMENT. mySql сам проследит за его уникальностью
Ответ написан
@MikhailEdoshin
Один из приемов генерации непоследовательных уникальных номеров — взять последовательный номер и применить к нему маскирующую операцию, например, инвертировать заданные биты, а затем переставить по фиксированной схеме. Пусть у нас будут трехбитные номера от 0 до 7: 000, 001, 010, 011, 100, 101, 110, 111. Сначала инвертируем второй бит: 010, 011, 000, 001, 110, 111, 100, 101. Получаются все те же номера, но уже в другом порядке — 2, 3, 0, 1, 6, 7, 4, 5. Затем, считая что биты нумеруются 321, переставим их в 132: 001, 101, 000, 100, 011, 111, 010, 110 — 1, 5, 0, 4, 3, 7, 2, 6.

Может быть, имеет смысл также добавить контрольную сумму, пусть хоть бит четности — 0010, 1010, 0001, 1001, 0110, 1110, 0101, 1101 или 2, 10, 1, 9, 6, 14, 5, 13. Числа остаются уникальными, и полностью обратимыми, но расползаются по большему диапазону.
Ответ написан
EugeneOZ
@EugeneOZ
Уберите странную приставку — она вообще никак не исключает дубли и ухудшает читаемость. Вам 4 символов хватит с головой, даже если магазин планирует несколько раз всем жителям планеты продать по товару (количество знаков в коде возведите в степень количества возможных символов, например 4^20). А уникальность нужно именно проверять в БД и делать блокировку, пока не записали туда новое значение — это очень серьёзный момент, Вы зря так халатно относитесь к нему.
Ответ написан
7workers
@7workers
когда-то решал точно такую же задачу, пришел к тому, что скрипт генерировал гарантированно уникальные ID и складывал в буффер, кому нужно было просто брали из буффера.
Ответ написан
Комментировать
Чем не устраивает autoincrement int?
Ответ написан
nochkin
@nochkin
Я бы сделал то же самое, но не на базе времени, а на базе id с базы данных.
То есть, мы не светим сам id по условиям задачи и результат всегда гарантированно уникален.
Так же можно к нему добавить тот же rand(10,99) если не хочется что бы заказы друг за другом ровно шли.
Ответ написан
creage
@creage
Я в своих проектах использую это. Можно переписать на любой язык.
Ответ написан
Комментировать
Комментировать
strib
@strib
Почему не счетчик и вставка разделителя через каждые 3 разряда?
С буквами — замучаетесь по телефону принимать номера заказов…
635-891
981-578-13
981-578-134
Ответ написан
Комментировать
7workers
@7workers
Кодируйте текущую дату-время + IP адрес с которого поступил заказ
Ответ написан
afiskon
@afiskon
«Проблема в том что используется база mongo где id такие 5149e333dffa825a03000047»

Например, так. Берите md5 от этого id (а также времени, IP пользователя и тп, по желанию) и оставляйте первые 8 символов. Если уже юзаются (что, впрочем, маловероятно), берите md5 от этого id и rand(), пока не получите уникальный id.
Ответ написан
Комментировать
Infra_HDC
@Infra_HDC
R2AMO
1. Попробуйте подумать в направлении использовать session_id() в качестве одного из источников для генерации хеша — в добавление к уже предложенным вариантам источников.
2. Если полученные хеши нечеловекочитаемы, то почему бы их не хранить где-нибудь в отдельной таблице — имя поля HASH для хранения самого хеша внутри этой таблицы — а для генерации человекочитаемой части id использовать целочисленное ID, т.е. ключ из этой таблицы?
3. Поддержание такой таблицы в актуальном состоянии
3.1. У каждого значения HASH есть время жизни, после которого он недействителен, возможно это время хранится в отдельном поле данной таблицы в формате timestamp
3.2. После того, как некий ID стал недействительным, есть технологический таймаут, после которого поле HASH очищается, и значение ID может использоваться повторно, либо запись удаляется полностью из таблицы
3.3. Либо, неактуальностью ID управляет сам прикладной алгоритм
При умелой реализации такого вот хозяйства, количество записей в таблице и максимальное значение его своего ID — должно быть не очень много, и с человекочитаемостью особых проблем возникнуть не должно: при поступлении заказов изначально ID будут 0, 1, 2, 3, 4, 5, ..., а при появлении дырок в последовательности, в результате удаления неактуальных ID — промежуточные значения между 1 и максимально достигнутом значением ID, т.к. дырки — вакантные области внутри упорядоченного множества значений ID — будут заполняться новыми данными.
Ответ написан
Комментировать
pshevchuk1964
@pshevchuk1964
java developer
Можна так String iD = (YYYYMMDDhhmmssSSS + random(5) + контрольная сумма)
public void createUnicalNumber() {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat format1 = new SimpleDateFormat("YYYYMMDDHHmmssSSS");
        cal.add( Calendar.DATE, (0) );
        String dataNew = format1.format( cal.getTime() );
        String dataAdd = createWord(5);
        String dataContr = contSumm(dataNew+dataAdd);
        do {
            dataContr = "0"+dataContr;
        } while (dataContr.length()<5);
        System.out.println( dataNew+dataAdd+dataContr );
    }

    private String contSumm( String str){
        int n_cont=0;
        for(int countStr =0; countStr<str.length();countStr++) {
            n_cont+=str.charAt( countStr );
    }
        String dxshxs=String.valueOf( n_cont );
        return dxshxs;
    }

    private String createWord (int l){
        String word ="";
        for (int x=0; x<l; x++ ) {
            int bukva = 65 +((int) Math.round(Math.random()*25));// 25 длина символьного ряда в кодовой таблице
            char one_char = (char) bukva;
            word +=  one_char;
        }
        return word;
    }

Выглядит так : 20180104113747719XKSAN01261
Виден момент формирования и дополненно контрольной суммой для исключения одиночной ошибки . Данный код можно проверить. Пишите - отвечу как.
Ответ написан
Комментировать
@Tomarev
Самый простой способ :)
$uniqueNum = crc32(uniqid());
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы