Выдача дубликата номера счета — фактуры при массовом вводе через несколько запросов на отправку завитков одновременно

#php #mysql #codeigniter #codeigniter-3

Вопрос:

Я пытаюсь вставить оптовые заказы через curl. Для каждого заказа создается номер счета-фактуры, увеличивающий ранее введенный номер счета-фактуры в таблице заказов.

Таблица Заказов

 Create Table tOrder (  OrderUID Int NOT NULL AutoIncrement,  OrderNumber Varchar(12) NOT NULL ,  CreatedBy Int,  CreatedOn DateTime,  Primary Key(OrderUID) );  

Примеры форматов порядковых номеров: ULEN21000001
UCMC21000002

Последние 6 цифр учитываются для увеличения последовательности при формировании заказа.

ВЫБЕРИТЕ справа(порядковый НОМЕР,6) В СПИСКЕ ЗАКАЗЫ ПО заказам.ОГРАНИЧЕНИЕ ПО ПОРЯДКУ 1;

Проблема, с которой я сталкиваюсь:
При вставке одного заказа или массовой вставки (из одного запроса на завиток) он работает. Но при вставке оптовых заказов, одновременно вызывая сообщение curl из разных систем. Повторяющиеся порядковые номера вставляются в таблицу заказов.
Например,
30 заказов из системы A и 15 заказов из системы B и так далее.

Выберите * из tOrder;

Ордерид OrderNumber Созданный
1 UABC210001 1
2 UABC210001 1
3 UABD210002 2
4 UABC210003 2
5 UABC210003 3
6 UABE210004 3
7 UABE210004 3

Что я пробовал:
1)В PHP я получаю весь запрос в массиве, перебираю каждую строку, генерирую порядковый номер и вставляю в таблицу tOrder.

 foreach($Orders as $order) {  $this-gt;db-gt;trans_begin();   $insArr =[  'OrderNumber' =gt; $this-gt;GenerateOrderNo(), //Order generation  'CreatedBy' =gt; 1,   ];   $this-gt;db-gt;insert('tOrder',$insArr);  $insert_id = $this-gt;db-gt;insert_id();   if ($this-gt;db-gt;trans_status() === false) {  $this-gt;db-gt;trans_rollback();  } else {  $this-gt;db-gt;trans_commit();  /* regenerating and updating if already exists */  if($this-gt;OrderExists($insArr['OrderNumber']))  {  $insArr =[  'OrderNumber' =gt; $this-gt;GenerateOrderNo(), //Order re-generation   ];  $this-gt;db-gt;where('OrderUID',$insert_id);  $this-gt;db-gt;update('tOrder',$insArr);  }  } }   
  1. В MySQL, Я написал триггер для генерации порядкового номера перед вставкой в таблицу.

СОЗДАЙТЕ ТРИГГЕР Insert_OrderNumber ПЕРЕД ВКЛЮЧЕНИЕМ ВСТАВКИ tOrders ДЛЯ КАЖДОЙ СТРОКИ, НАЧНИТЕ
ВЫБИРАТЬ справа(порядковый НОМЕР,6) В @LastOrderNo
ИЗ ЗАКАЗОВ ПО ЗАКАЗАМ.ОГРАНИЧЕНИЕ ПО ПОРЯДКУ 1;

ВЫБЕРИТЕ LPAD(@LastOrderNo 1, 6,0) В @NewSequenceNo;
УСТАНОВИТЕ НОВЫЙ.Порядковый номер = @NewSequenceNo; КОНЕЦ

Я также попытался добавить уникальный индекс для столбца с порядковым номером. В таком случае OrderEntry завершается с ошибкой.

Пожалуйста, поправьте меня, где я ошибаюсь, или какие-либо альтернативные предложения.

.

Комментарии:

1. Используйте столбец автоинкремента для числовой части и сохраните префиксные символы в отдельном столбце. Объедините их в запросе ВЫБОРА, если вам нужно увидеть полный идентификатор. С помощью автоинкремента база данных гарантирует вам уникальные значения

2. @ADyson Tha если заказ будет удален и снова создан, то продолжение номера заказа станет проблемой при таком подходе. Попробую ваше предложение.

3. Что именно вы подразумеваете под «продолжением номера заказа»? Неясно, что вы описываете или почему это будет проблемой. Пожалуйста, объясните свое беспокойство.

4. @ADyson Требовал непрерывного номера. Например, 1 — й заказ — C2TM21000001, 2-й заказ-D3TN21000002 и т. Д. Z5TN21000N (N = последний номер). Здесь последние 6 цифр являются непрерывными, несмотря на префикс (первые 6 цифр). В случае автоматического увеличения, если я удалю последний номер и создам новый заказ, то сгенерированный номер будет N 1. На данный момент текущая логика работает, когда запрос поступает из одной системы. Фактическая проблема с дублированием возникает только в том случае, если запрос поступает из нескольких систем одновременно.

5. …И поля автоматического увеличения существуют по нескольким причинам-во — первых, для решения проблемы с условиями гонки, с которой вы в настоящее время сталкиваетесь при одновременном добавлении строк из нескольких систем, и, во-вторых, для обеспечения того, чтобы идентификаторы были уникальными для сущности в таблице и не могли быть повторно использованы (случайно или специально), независимо от того, была ли удалена запись. Есть и другие причины. Но в принципе — они решают вашу проблему, поэтому, пожалуйста, используйте их. Постоянное наличие непрерывных чисел не является полезным требованием — на самом деле это вызывает проблемы, а не решает их, как я уже объяснял.

Ответ №1:

Я бы предложил изменить структуру таблицы таким образом, чтобы вы сохраняли префикс заказа вместо всего номера заказа (6 символов вместо 12 и не сохраняли последовательность дважды в одной таблице). —

 CREATE TABLE `tOrder` (  `OrderUID` INT UNSIGNED NOT NULL AUTO_INCREMENT,  `OrderPrefix` CHAR(6) NOT NULL,  `CreatedBy` INT UNSIGNED NOT NULL,  `CreatedOn` DATETIME NOT NULL,  PRIMARY KEY (`OrderUID`));  

Нет необходимости в другой последовательности для номера заказа, так как у вас уже есть столбец OrderUID. Поэтому при доступе к таблице вы можете просто —

 SELECT  OrderUID,  CONCAT(OrderPrefix, LPAD(OrderUID, 6, '0')) AS OrderNumber,  CreatedBy,  CreatedOn FROM tOrder;  

или просто сохраните этот запрос в виде представления —

 CREATE VIEW `vw_orders` AS SELECT  OrderUID,  CONCAT(OrderPrefix, LPAD(OrderUID, 6, '0')) AS OrderNumber,  CreatedBy,  CreatedOn FROM tOrder;  

Вы не включили источник для своего GenerateOrderNo() метода в свой исходный пост. Я подозреваю, что было бы разумным подходом удалить исправление OrderPrefix из таблицы tOrder и заменить его внешним ключом к объекту, на который ссылается ваш префикс заказа.

Комментарии:

1. Привет @nnichols, спасибо. Я не включил GenerateOrderNo(), потому что он похож на триггер, о котором я упоминал. Просто возьмите последние 6 цифр предыдущего порядка, увеличьте их и добавьте префикс. Я почти не сомневаюсь в таком подходе. Предположим, что если заказ будет удален и снова создан, то продолжение номера заказа будет пропущено. Независимо от префикса, требуется порядковый номер продолжения. Здесь повторяющиеся номера вставляются только тогда, когда функция запускается параллельно из разных систем.

2. @DSP Проблема с вашим методом управления последовательностью заключается в том, что он небезопасен при одновременных запросах. Несколько клиентов ВЫБЕРУТ одно и то же значение Lastordername, поэтому при установке НОВОГО они используют одно и то же числовое значение. Порядковый номер, будь то в вашем PHP-коде или в триггере. Чтобы ваш метод работал, вам необходимо заблокировать чтение и последующую вставку, чтобы ни один другой сеанс клиента не мог прочитать то же значение. Тогда это означало бы, что у вас может быть множество запросов на блокировку вашей таблицы торговцев.

3. Я не понимаю вашей потребности в непрерывных номерах заказов. Вероятно, вам не следует разрешать жесткое удаление заказов, так как это потенциально может привести к нескольким заказам с одним и тем же числовым идентификатором — исходный удален, а затем создан новый заказ с тем же идентификатором.