Как выбрать случайный и уникальный ID в mysql

Тема в разделе "Базы данных", создана пользователем verfaa, 31 мар 2013.

Статус темы:
Закрыта.
Модераторы: latteo
  1. verfaa

    verfaa

    Регистр.:
    29 янв 2007
    Сообщения:
    375
    Симпатии:
    41
    В таблице есть поле orders_id
    в нём хранятся числовые id заказов в таком виде:

    29
    457
    3467844
    235
    2345
    33467899
    331136
    9272

    Т.е. случайные числа не по-порядку.
    Каким образом (с помощью запроса mysql) можно сгенерировать случайное число в диапазоне 10-10000000, которого НЕТ в поле (т.е. уникальное для этого поля)?
     
  2. Nei

    Nei Nosce te ipsum

    Регистр.:
    5 сен 2009
    Сообщения:
    635
    Симпатии:
    502
    Как вариант - отсортировать по возрастанию и добавить еденицу к последнему значению
     
  3. verfaa

    verfaa

    Регистр.:
    29 янв 2007
    Сообщения:
    375
    Симпатии:
    41
    Это тоже не вариант, если максимальное значение окажется слишком большим, скажем 10 млн. будет не совсем удобно с ним работать.

    Мне тут настойчиво советуют auto_increment, поэтому дополню свой вопрос:
    Когда пользователь переходит на страницу заказа я должен сгенерировать уникальный ID заказа и вписать его в скрытое поле <input type='hidden' name='ornum' value='265895' /> на странице заказа.

    Этот ID я передаю биллингу, когда пользователь кликает "оплатить" и попадает на сайт биллинга.

    На стадии оформления заказа биллинг передаёт мне этот ID обратно в скрытый файл Result.php и, если все успешно я вписываю этот ID к себе в БД MySQL вместе с другой информацией по платежу.

    Теперь вопрос, если к примеру на сайте было 20 заказов, пользователь перешел на страницу заказа, я вписал в скрытое поле следующий незанятый ID - 21. В это время заказ начали оформлять ещё 5 пользователей - им тоже вписал скрытое поле ID - 21, ведь он ещё не занят (биллинг не обращался на Result.php и в БД этот ID не вписан). В результате я получу ситуацию когда в какой то момент пользователь оформлявший заказ, завершил его вписав ID 21 в БД. А следующий сразу за ним другой пользователь, тоже получивший ID 21 будет пытаться вписать в БД дублирующую запись и получит ошибку, если поле будет, как мне советуют auto_increment.
     
  4. latteo

    latteo Эффективное использование PHP, MySQL

    Moderator
    Регистр.:
    28 фев 2008
    Сообщения:
    1.549
    Симпатии:
    1.431
    rand не решит полностью проблему с возможным дублированием (в лотереи ведь люди выигрывают :) )
    тебе всё же стоит вести таблицу с auto_increment

    Между этими двумя пунктами твой обработчик на php? Если да создавай новый заказ с пометкой не оплачено и передавай биллингу id из таблицы.
     
    verfaa и Шумадан нравится это.
  5. Nei

    Nei Nosce te ipsum

    Регистр.:
    5 сен 2009
    Сообщения:
    635
    Симпатии:
    502
    Можно использовать таки rand, но добавить на всякий пожарный проверку на уникальность сгенерированного ключа и перегенерить, если такой ключ уже есть.
    Правда это дополнительный запрос в базу, а значит допнагрузка.
     
  6. badnn

    badnn Писатель

    Регистр.:
    18 янв 2010
    Сообщения:
    6
    Симпатии:
    1
    Создать переменную табличного типа, заполнить её в цикле нужным диапазоном, заджойнить с целевой таблицей левым соединением, исключить совпадающие и взять топ 1. Этот селект должнен быть значением для инсерта в целевую таблицу, иначе могут быть коллизии при больших нагрузках. А по уму лучше действительно инкремент использовать.
     
    Шумадан нравится это.
  7. Шумадан

    Шумадан Хабарра!!11

    Регистр.:
    6 фев 2008
    Сообщения:
    1.728
    Симпатии:
    2.105
    тоесть, нельзя взять и привести к нормальному виду заказы? сделать поле order_id_inc автоинкрементом и проапдейтить все связи, а потом переименовать в orders_id? правда, да, логику нужно будет чуток поправить. но это дело скажем одного дня, чем потом опять на грабли наступать (если таковые есть) и опять искать какието решения.

    просто прикинуть, что в определённый момент будет 100500100500 очень много записей и процедура генерации айди будет всё медленнее и медленнее, это в случае если будете искать рендом которого ещё нет в базе
     
  8. nethare

    nethare Постоялец

    Регистр.:
    27 окт 2012
    Сообщения:
    104
    Симпатии:
    30
    Генератор случайных чисел с последующей проверкой нельзя использовать. Есть ненулевая вероятность того, что подбор не закончится никогда. Да и с ростом количества значений в таблице время подбора будет увеличиваться.

    Что это за магазин такой, где код заказа без авто_инкремента? Разве что это вопрос безопасности (типа чтоб нельзя было угадать код следующего заказ). Но мне кажется, что если б все было настолько серьезно, то ТС не просил бы помощи..
     
    Шумадан нравится это.
  9. tan_81

    tan_81 Постоялец

    Регистр.:
    18 окт 2006
    Сообщения:
    128
    Симпатии:
    30
    Если по сути топик-стартера, то тут явно кривой движек и нужен тупо инкремент.
    А по поводу рандома - в свое время для купонного сервиса было придумано следующее решение - hex(xx...xxx.hh.dd.mm.yyyy.xx...xxx) + проверка на уникальность (xx...xx - это некий рандомный диапазон). При огромном числе оных совпадений не было ни 1. С числами можно то же самое сделать (хоть timestamp брать с довеском), просто очень большие величины.
     
  10. nethare

    nethare Постоялец

    Регистр.:
    27 окт 2012
    Сообщения:
    104
    Симпатии:
    30

    У вас хоть hex был, а у ТС целые числа. Повторюсь, при рандомном подборе с проверками существует НЕНУЛЕВАЯ вероятность того, что подбор будет вечным. Да, эта вероятность очень-очень низка, но тем не менее существует. На продакшне такого нельзя допускать. А вдруг заказ на миллион, а клиент сидит как баран и ждет.

    Если действительно нужен элемент случайности - брать максимум и добавлять к нему rand(10), ну или rand(100), насколько позволит совесть и размерность поля в базе.

    Но и это тоже на самом деле плохо. При сотне тысяч записей mysql'у надоест их сортировать. Да и хостер за такое по голове не погладит.

    Лучшее решение - AUTO_INCREMENT в любом случае.
     
    Шумадан нравится это.
Статус темы:
Закрыта.