Приветствую! Подскажите как сделать уникальный номер заказа - A001, А002, А003.. когда число доходит до А999, меняем букву В001, В002 и тд, все это будет записываться в БД, где нужно проверить номер на уникальность
Сгенерируй таблицу в БД с двумя полями идентификатор и желаемые номера. Обращайся к таблице когда надо получить очередной номер.
Нафига себе гемморой придумывать? Использовать в качестве номера заказа обычный id autoincrement который уникальный в БД но если хочется прям извращаться 1. поле в БД - unique 2. Если нет ни одной записи - генерим первую 3. Если есть - считываем последнюю, разбиваем на первый символ и число, числ + 1, если выходит за 999, то число = 001 а символ первый на следующий меняем но это такой гемор... а что - когда символы кончатся? а это обязательно случится + 1 запрос всегда для формирования нового номера заказа
Никакого геморроя нет. полный номер может выглядеть например так 2019-А001 или так 2019-11-А015 такие номера гораздо легче воспринимаются человеком в отличии от голого id autoincrement (который должен быть в самой системе обязательно, но на уровне кода)
PHP: function getNextOrder($prev) { $res = preg_match('/^([a-z]+)(\d+)$/i', $prev, $matches); if(!$res) { throw new Exception('Wrong order format'); } list(,$prefix, $num) = $matches; if($num == 999) { $prefix++; $num = 0; } return $prefix . sprintf('%03d', ++$num); } echo getNextOrder('Z999'); // AA001
@zhenia3003, например, в MySql есть last_insert_id(). Тогда можно сделать таким образом: Код (Text): -- табличка: CREATE TABLE `sequence` ( `letter` varchar(1) NOT NULL, `number` int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- инициализация insert into sequence values('A',0); -- триггер CREATE TRIGGER `sequence_BEFORE_UPDATE` BEFORE UPDATE ON `sequence` FOR EACH ROW begin if new.number > 999 then set new.number = 1; set new.letter = char(ord(old.letter)+1); end if; end -- получение нового значения: update sequence set number = last_insert_id(`number` + 1); select concat(`letter`,'-',lpad(case when last_insert_id()=1000 then 1 else last_insert_id() end,3,'0')) as docnumber from sequence; Впрочем, как всегда - надо проверять
Триггеры, спец таблицы... вам не кажется что вы слишком усложняете? В чем суть задачи? Показать юзеру короткий и удобный, при этом уникальный номер заказа. Плюс хочется чтобы на уровне БД, для заказов, юзался быстрый INTEGER ключ. так? Всё элементарно. У нас есть таблица заказов, и там УЖЕ есть PK. те дополнительно ничего придумывать в БД не надо. осталось придумать как показать юзеру красивый номер заказа. и вот это мы можем делать уже на уровне php, например так: PHP: function idToHumanFriendly($orderId) { $prefix = floor($orderId/999); $orderTail = $orderId - 999 * $prefix; // Convert integer to letters $letters = array_merge(range('A', 'Z')); $base = sizeof($letters); $head = ''; while($prefix >= $base) { $d1 = floor($prefix/$base); $ost = $prefix-$d1*$base; $prefix = $d1; $head .= $letters[$ost]; } return strrev($head . $letters[$prefix]) . sprintf('%03d', $orderTail); } $orderId = 102304; // ID from DB $humanFriendlyOrderId = idToHumanFriendly($orderId); echo $humanFriendlyOrderId; // DY406 и всё! все довольны.
@runcore, триггеры лишние совершенно согласен. Но вот на счёт всего остального, я вообще не понимаю почему приходится что-то объяснять. @runcore, уникальный номер заказа, это не просто " показать на сайте". Своими махинациями, вы можете убить уникальность.
уникальность обеспечивается свойствами PK INTEGER autoincrement. Функция реализует лишь дополнительный вариант отображения(удобный для человека), упаковывая число в структуру с большим количеством возможных символов.
Вы решаете задачу как программисты. А ещё есть правила документопроизводства. В частности, вполне могут быть правила строгой последовательности нумерации. И тогда, если дырка в нумерации, то это ЧП, ибо это потеря документа. С другой стороны, ТС просто спросил, как сделать такую, буквенно-цифровую, нумерацию, без особых ограничений. И в этом случае самый простой вариант будет самым оптимальным. Кстати, поле с AUTO_INCREMENT использовать для нумерации документов нельзя в принципе. Независимо, допустимы дырки или нет.
Если на уровне PHP, то можно использовать его особенность. Как у @runcore, только проще. Например, так: PHP: $docNum = 'A999'; // Это предыдущий номер $docNum = preg_replace('/0$/', '1', ++$docNum); // B001
Автоинкремент может меняться как хочет и когда хочет - база не пострадает, если всё сделано правильно. А номер документа назначается единожды и не может быть изменён (пресловутое делопроизводство). --- Добавлено --- Убеди.
Я надеялся услышать что-то более вменяемое. Ты не путаешь процесс и состояние? Что значит автоинкремент может меняться? Мы создали запись с определенным ключём. Гарантируется что это значение уникально. Пусть автоинкремент после этого меняется как угодно. Плевать. Первичный ключ по определению уникален.
Предполагаю, что это ты путаешь базу с делопроизводством. Первичен Документ, а база это просто информация о нём. Почитай, например, о правилах ведения бухгалтерии предприятий, для понимания.
Я ничего не путаю. Это ты неудачно ответил, а теперь уворачиваешся. Я про делопроизводство знаю столько, что мог бы народ учить. Сертифицированный разработчик и админ ЭДО. Только не моё это.
Это обычное поведение @Chushkin'a. За много лет удачных ответов я от него не видел вообще. Тут даже по теории вероятности они должны были бы быть, но их нет.
@Дюран, даже если бы он работал, он всё равно логически не верный. @runcore предложил единственно верный вариант - это строгое соответствие идентификатору (я сначала не понял, думал он тоже инкрементит, но ошибся), о котором я говорил с самого начала, только я говорил о том, что нет нужды тащить это на уровень РНР, всё можно прекрасно сделать на уровне СУРБД.
Угу, не работает. А так? PHP: $docNum = 'A999'; // Это предыдущий номер $docNum = preg_replace('/(\D[0]*)0$/U', '${1}1', ++$docNum); // B001 $docNum = 'A099'; $docNum = preg_replace('/(\D[0]*)0$/U', '${1}1', ++$docNum); // A100
Спойлер: Вот как-то так PHP: <?php error_reporting(E_ALL); ini_set('display_errors', 'On'); header('Content-Type: text/html; charset=utf-8'); /** * Модель */ // Подключение к базе данных // можно вынести в отдельный файл вместе с параметрами подключения // и подключать в скриптах при помощи require_once function connectDB(){ $mysqli = @new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME); if ($mysqli->connect_error) { throw new Exception('Невозможно подключиться к серверу БД: ' . $mysqli->connect_error); } $mysqli->query('set names utf8'); if ($mysqli->error) { throw new Exception('Невозможно установить кодировку подключения к БД: ' . $mysqli->error); } return $mysqli; } // Создание таблицы с номерами заказов function createIdentifierTable($mysqli) { $mysqli->query("CREATE TABLE IF NOT EXISTS `test`.`identifier` ( `id` INT(5) NOT NULL , `order` CHAR(4) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;"); if ($mysqli->error) { throw new Exception('Ошибка создания таблицы: ' . $mysqli->error); } $mysqli->query("TRUNCATE TABLE `test`.`identifier`"); if ($mysqli->error) { throw new Exception('Ошибка очистки таблицы: ' . $mysqli->error); } $counter = 0; for($i = 65; $i <= 90; $i++) { for($n = 1; $n < 1000; $n++) { $insertValue[++$counter] = "(" . $counter . ", '" . chr($i) . sprintf('%03d', $n) . "')"; } } $query = "INSERT INTO `test`.`identifier` (`id`, `order`) VALUES " . implode(', ', $insertValue); $mysqli->query($query); if ($mysqli->error) { throw new Exception('Ошибка добавления данных в таблицу: ' . $mysqli->error); } } // Создание таблицы заказов function createOrderTable($mysqli) { $mysqli->query("CREATE TABLE IF NOT EXISTS `test`.`order` ( `id` INT(5) NOT NULL AUTO_INCREMENT , `order` CHAR(4) CHARACTER SET utf8 COLLATE utf8_general_ci NULL , `title` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;"); if ($mysqli->error) { throw new Exception('Ошибка создания таблицы: ' . $mysqli->error); } $mysqli->query("TRUNCATE TABLE `test`.`order`"); if ($mysqli->error) { throw new Exception('Ошибка очистки таблицы: ' . $mysqli->error); } } // Получение заказов из базы данных function getOrder($mysqli) { $query = "SELECT `id`, `order`, `title` FROM `order` WHERE `order` IS NOT NULL"; $result = $mysqli->query($query); if ($mysqli->error) { throw new Exception('Ошибка выборки из базы данных: ' . $mysqli->error); } if ($result->num_rows > 0) { while ($row = $result->fetch_array(MYSQLI_ASSOC)) { $table[] = '<td>' . $row['id'] . '</td><td>' . $row['order'] . '</td><td>' . $row['title'] . '</td>' . '<td><a href="?action=del_order&id=' . $row['id'] . '">Удалить</a></td>' . PHP_EOL; } return '<tr>' . PHP_EOL . implode('</tr>' . PHP_EOL . '<tr>' . PHP_EOL , $table) . '</tr>' . PHP_EOL; } } // Создание заказа function addOrder($mysqli, $title) { if (empty($title)) { return false; } $title = $mysqli->real_escape_string($title); $query = "INSERT INTO `order` (`title`) VALUES('" . $title . "')"; $result = $mysqli->query($query); if ($mysqli->error) { throw new Exception('Ошибка добавления записи в базу данных: ' . $mysqli->error); } if ($mysqli->affected_rows > 0) { $query = "UPDATE `order` o LEFT JOIN `identifier` i USING (`id`) SET o.order = i.order WHERE o.id = LAST_INSERT_ID()"; $result = $mysqli->query($query); if ($mysqli->affected_rows > 0) { return true; } throw new Exception('Не удалось присвоить уникальный номер заказа'); } } // Удаление заказа function delOrder($mysqli, $id) { if ($id = (int)$id) { $query = "UPDATE `order` SET `order` = NULL WHERE id = " . $id . " LIMIT 1"; $result = $mysqli->query($query); if ($mysqli->error) { throw new Exception('Ошибка "удаления" заказа из базы данных: ' . $mysqli->error); } return true; } return false; } // Восстановление заказа function restOrder($mysql, $id) { // В качестве домашнего задания написать функцию восстановления заказа } /** * Контроллер */ try { // Инициализация переменных $table = false; $action = false; $template = false; $array_action = [ 'order' => 'order', 'add_order' => 'add_order', 'del_order' => 'del_order', ]; // Параметры подключения к базе данных define('DB_HOST', 'localhost'); define('DB_USER', 'mysql'); define('DB_PASSWORD', 'mysql'); define('DB_NAME', 'test'); // Устанавливаем соединение с базой данных $mysqli = connectDB(); // Инициализация таблиц базы данных if ($_REQUEST['create_db']??false == 'yes') { createOrderTable($mysqli); createIdentifierTable($mysqli); header("Location: " . $_SERVER['PHP_SELF'] . '?action=order'); } // Обработка параметра action if ($_REQUEST['action']??false) { $action = $array_action[$_REQUEST['action']]??false; } switch ($action) { case 'order': $template = 'order'; $table = getOrder($mysqli); break; case 'add_order': $template = 'add_order'; $title = trim($_POST['title']??false); if (!empty($title) && addOrder($mysqli, $title)) { header("Location: " . $_SERVER['PHP_SELF'] . '?action=order'); } break; case 'del_order': $id = $_GET['id']??false; if (!empty($id) && delOrder($mysqli, $id)) { header("Location: " . $_SERVER['PHP_SELF'] . '?action=order'); } $template = 'order'; $table = getOrder($mysqli); break; default: } } catch (Exception $e) { echo $e->getMessage(); } /** * Шаблоны страниц */ ?> <html> <head> <title></title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> </head> <body> <div class="container"> <hr /> <div class="row"> <div class="col-md"> <small> <h5>Отладочная информация</h5> <hr /> <h6 class ="table-danger">Массив $_GET <?php echo 'count ('.count($_GET).')';?></h6> <pre><?php print_r($_GET);?></pre> <hr /> <h6 class ="table-danger">Массив $_POST <?php echo 'count ('.count($_POST).')';?></h6> <pre><?php print_r($_POST);?></pre> </small> </div> <div class="col-md-9"> <? switch ($template): case 'order': ?> <h2>Список заказов</h2> <table class="table"> <?=$table?> </table> <a href="?action=add_order">Новый заказ</a> <? break; ?> <? case 'add_order': ?> <h2>Форма создания нового заказа</h2> <hr /> <form method="POST"> <div class="form-group"> <input type="hidden" name="action" value="add_order"> <label for="title">Введите название заказа</label> <input type="text" class="form-control" id="title" aria-describedby="titleHelp" name="title"> <small id="titleHelp" class="form-text text-muted">Не более 100 символов</small> </div> <button type="submit" class="btn btn-primary">Создать</button> </form> <hr /> <a href="?action=order">Вернуться к списку заказов</a> <? break; ?> <? default: ?> <hr /> <ul> <li><a href="?action=order">Список заказов</a></li> <li><a href="?action=add_order">Новый заказ</a></li> <li><a href="?create_db=yes">Инициализация БД</a></li> </ul> <? endswitch; ?> </div> </div> <hr /> </div> </body> </html> </body> </html> Основная функция создания заказа, всё остальное "обвес" для демострации PHP: // Создание заказа function addOrder($mysqli, $title) { if (empty($title)) { return false; } $title = $mysqli->real_escape_string($title); $query = "INSERT INTO `order` (`title`) VALUES('" . $title . "')"; $result = $mysqli->query($query); if ($mysqli->error) { throw new Exception('Ошибка добавления записи в базу данных: ' . $mysqli->error); } if ($mysqli->affected_rows > 0) { $query = "UPDATE `order` o LEFT JOIN `identifier` i USING (`id`) SET o.order = i.order WHERE o.id = LAST_INSERT_ID()"; $result = $mysqli->query($query); if ($mysqli->affected_rows > 0) { return true; } throw new Exception('Не удалось присвоить уникальный номер заказа'); } }
@Valick Это для задачи строгой последовательности нумерации документов. Базовая мысль правильная, но решение на уровне юниора. Фактически, не рабочее. Попытайся улучшить.