Intereting Posts
Как запустить PHP-код в фоновом режиме на сервере unix PHP, MYSQL: упорядочить по дате, но пустые даты не последним Программно изменить переменную от публичной к частной Как я могу изменять параметры в выпадающих меню, если они генерируются динамически? iOS7 – квитанции, не проверяющие в песочнице – ошибка 21002 (java.lang.IllegalArgumentException) PHP PDO + Подготовить отчет MySQL LEFT JOIN Проблема с несколькими таблицами ошибка phpunit при тестировании реализации с инъецированными зависимостями Как исправить android.os.NetworkOnMainThreadException? .htaccess доступен только через ip PHP: Может ли массив иметь массив как ключ в паре ключ-значение? Почему этот PHP-код просто эхо «Array»? Представление формы Ajax в symfony2 с изящной деградацией для пользователей без javascript Как получить все после определенного персонажа? Правильный способ установки driverOptions для конфигурации DBAL Doctrine в symfony2

MysqlError: повторяющаяся запись '1-5' для ключа 'PRIMARY' для вставки unsure of how

Я получаю сообщение об ошибке MysqlError: Duplicate entry '1-5' for key 'PRIMARY' как показано ниже в коде. Это произошло только один раз (что я мог обнаружить, но это было случайным), и я не мог найти причину (сообщал New Relic), но я не могу воспроизвести, и у меня нет гораздо больше информации, кроме номера строки и ошибки , Схема и код ниже.

num_rows() как-то возвращает значение, которое не равно 1, хотя оно не должно. Если кто-то может дать некоторое представление о том, как отлаживать или исправлять это, было бы полезно.

Вот моя схема для location_items:

 CREATE TABLE `phppos_location_items` ( `location_id` int(11) NOT NULL, `item_id` int(11) NOT NULL, `location` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '', `cost_price` decimal(23,10) DEFAULT NULL, `unit_price` decimal(23,10) DEFAULT NULL, `promo_price` decimal(23,10) DEFAULT NULL, `start_date` date DEFAULT NULL, `end_date` date DEFAULT NULL, `quantity` decimal(23,10) DEFAULT '0.0000000000', `reorder_level` decimal(23,10) DEFAULT NULL, `override_default_tax` int(1) NOT NULL DEFAULT '0', PRIMARY KEY (`location_id`,`item_id`), KEY `phppos_location_items_ibfk_2` (`item_id`), CONSTRAINT `phppos_location_items_ibfk_1` FOREIGN KEY (`location_id`) REFERENCES `phppos_locations` (`location_id`), CONSTRAINT `phppos_location_items_ibfk_2` FOREIGN KEY (`item_id`) REFERENCES `phppos_items` (`item_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci | 

И код:

 //Lock tables involved in sale transaction so we do not have deadlock $this->db->query('LOCK TABLES '.$this->db->dbprefix('customers').' WRITE, '.$this->db->dbprefix('receivings').' WRITE, '.$this->db->dbprefix('store_accounts').' WRITE, '.$this->db->dbprefix('receivings_items').' WRITE, '.$this->db->dbprefix('giftcards').' WRITE, '.$this->db->dbprefix('location_items').' WRITE, '.$this->db->dbprefix('inventory').' WRITE, '.$this->db->dbprefix('people').' READ,'.$this->db->dbprefix('items').' WRITE ,'.$this->db->dbprefix('employees_locations').' READ,'.$this->db->dbprefix('locations').' READ, '.$this->db->dbprefix('items_tier_prices').' READ , '.$this->db->dbprefix('location_items_tier_prices').' READ, '.$this->db->dbprefix('items_taxes').' READ, '.$this->db->dbprefix('item_kits').' READ , '.$this->db->dbprefix('location_item_kits').' READ, '.$this->db->dbprefix('item_kit_items').' READ, '.$this->db->dbprefix('employees').' READ , '.$this->db->dbprefix('item_kits_tier_prices').' READ , '.$this->db->dbprefix('location_item_kits_tier_prices').' READ, '.$this->db->dbprefix('suppliers').' READ, '.$this->db->dbprefix('location_items_taxes').' READ , '.$this->db->dbprefix('location_item_kits_taxes'). ' READ, '.$this->db->dbprefix('item_kits_taxes'). ' READ'); // other code for inserting data into other tables that are not relevant. foreach($items as $line=>$item) { $cur_item_location_info->quantity = $cur_item_location_info->quantity !== NULL ? $cur_item_location_info->quantity : 0; $quantity_data=array( 'quantity'=>$cur_item_location_info->quantity + $item['quantity'], 'location_id'=>$this->Employee->get_logged_in_employee_current_location_id(), 'item_id'=>$item['item_id'] ); $this->Item_location->save($quantity_data,$item['item_id']); } // other code for inserting data into other tables that are not relevant. $this->db->query('UNLOCK TABLES'); class Item_location extends CI_Model { function exists($item_id,$location=false) { if(!$location) { $location= $this->Employee->get_logged_in_employee_current_location_id(); } $this->db->from('location_items'); $this->db->where('item_id',$item_id); $this->db->where('location_id',$location); $query = $this->db->get(); return ($query->num_rows()==1); } function save($item_location_data,$item_id=-1,$location_id=false) { if(!$location_id) { $location_id= $this->Employee->get_logged_in_employee_current_location_id(); } if (!$this->exists($item_id,$location_id)) { $item_location_data['item_id'] = $item_id; $item_location_data['location_id'] = $location_id; //MysqlError: Duplicate entry '1-5' for key 'PRIMARY' return $this->db->insert('location_items',$item_location_data); } $this->db->where('item_id',$item_id); $this->db->where('location_id',$location_id); return $this->db->update('location_items',$item_location_data); } } function get_logged_in_employee_current_location_id() { if($this->is_logged_in()) { //If we have a location in the session if ($this->session->userdata('employee_current_location_id')!==FALSE) { return $this->session->userdata('employee_current_location_id'); } //Return the first location user is authenticated for return current($this->get_authenticated_location_ids($this->session->userdata('person_id'))); } return FALSE; } 

Не рекомендуется проверять существование до ввода данных вне транзакции, так как это оставляет возможность изменения данных в среднем времени. Тот факт, что вы однажды видели эту ошибку, но не легко повторяется, заставляет меня задаться вопросом, могло ли это произойти.

Предложил бы изменить код ниже первого блока if в функции save вместо того, что генерирует следующий SQL: INSERT INTO location_items (item_id, location_id) VALUES ( $ item_id , $ location_id ) ON DUPLICATE KEY UPDATE

Это охватывает проверку наличия и вставка или обновление в одном атомном заявлении. (Чтобы принять это в дальнейшем и сказать, как его реализовать, мне нужен доступ к коду db .)

EDIT: Извините, только что заметил, что код db – CodeIgniter. Я новичок в этой структуре, но приведенный выше метод выглядит совершенно возможным из краткого обзора. Что-то вроде этого:

 $sql = "INSERT INTO location_items (item_id, location_id)" . " VALUES (?, ?)" . " ON DUPLICATE KEY UPDATE"; $this->db->query($sql, array($item_id, $location_id)); 

(Если по какой-то причине вы предпочитаете не делать этого, другой способ сохранить его атомарным будет заключаться в том, чтобы обернуть операторы внутри транзакции ( $this->db->trans_start(); перед проверкой существования и $this->db->trans_complete(); после вставки / обновления. Но IMO это вводит ненужную сложность – лично предпочитает первый метод.)

Похоже на состояние гонки. Скорее всего, это примерно одновременные звонки:

 save($data,5); 

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

Вы не получите решение, если существуют следующие условия:

  1. Вы не можете воспроизвести эту проблему самостоятельно.
  2. Вы не сообщаете свой исходный код и базу данных, чтобы кто-то еще попытался реплицировать.

Я не прошу вас поделиться полным исходным кодом. Скорее, я говорю это, чтобы умерить ваши ожидания.

При этом дубликаты могут существовать по многим причинам. Это поможет вам ответить на вопрос, предоставили ли вы свою версию, но я нашел одну причину, которая может быть причиной: слишком низкая память – может быть воспроизводимой, если вы опустите свою память или нанесете большую нагрузку на вашу систему. Если вам нелегко было его воспроизвести, возможно, память может быть причиной того, что вы, возможно, не пытаетесь имитировать это.

Другие вещи, которые необходимо учитывать:

  • Вы можете тратить свое время на попытку дублировать то, что просто не будет дублироваться.
  • Если вас это волнует, вы снова столкнетесь с этой проблемой, вы должны действительно рассмотреть возможность ведения журнала . Это поможет вам отследить запрос, вызвавший проблему. Я бы посоветовал вам не регистрироваться в производственной среде и только в разработке, потому что это, вероятно, приведет к штрафам за производительность, которые могут быть значительными. Если это одноразовая проблема, вы, возможно, никогда не увидите ее снова, но это не помешает быть подготовленным и вооруженным дополнительной информацией, если проблема снова появится.

В конечном итоге отладка требует возможности воспроизвести ошибку. Ошибка является частью компьютерной программы, что означает наличие определенных ситуаций и условий, в которых это произойдет, которые могут быть воспроизведены. Когда вы не представляете, как и почему возникла ошибка, от нее некуда вернуться. Полезно рассматривать вспомогательные проблемы как потенциальный источник вашей проблемы, изучая отчеты об ошибках и т. Д. Если это не удается, внедрите такие инструменты, как ведение журнала, которые дают вам больше информации. Это единственный способ найти основную причину этой проблемы или получить более конкретную информацию от сообщества SO о том, как это сделать.

Я подозреваю, что проблема может быть связана с кешем, где содержатся утверждения. Этот подозреваемый исходит из этого вопроса stackoverflow .

В принципе, я думаю, что может случиться так:

 - in one cycle this code is executed at the end of save method: $this->db->where('item_id',$item_id); $this->db->where('location_id',$location_id); return $this->db->update('location_items',$item_location_data); - in the subsequent cycle this code is executed in the exists method: $this->db->where('item_id',$item_id); $this->db->where('location_id',$location_id); return $this->db->update('location_items',$item_location_data); 

При выполнении кода «существует» кеш может по-прежнему содержать предложения where предыдущего оператора, а новый (другой) будет добавлен. Таким образом, результат будет пустым и кажется, что строка не находится в таблице.

Попытайтесь использовать $this->db->flush_cache(); после обновления в методе сохранения.

Также попробуйте использовать echo $this->db->last_query(); чтобы увидеть, что пытается сделать в существующем запросе.

может быть таблица «phppos_location_items» существует в прошлом, и в этой таблице выполняется инструкция delete Delete from phppos_location_items; в этом случае столбец первичного ключа не принимает предыдущие значения, если вы обрезаете таблицу, тогда вся предыдущая запись будет удалена

Извините, его немного длинный комментарий.

Что подает форму? Я предполагаю, что его кнопка находится где-то на странице. Когда у меня была аналогичная ошибка, она была вызвана двойным щелчком пользователя по кнопке и двумя запросами, которые были отправлены, в очень близкое время друг к другу. Это вызвало проверку, аналогичную вашей, чтобы иметь такую ​​ситуацию

 Request 1 Check Insert Request 2 Check Insert 

Поскольку Request 2 был последним запросом (поскольку второй щелчок занял приоритет), ошибка была показана, хотя первый запрос завершил всю работу.

я использую код

 $("form").submit(function() { $(this).submit(function() { return false; }); return true; }); 

от этого вопроса

Как предотвратить отправку формы несколько раз со стороны клиента?

Попробуй использовать

 $insert = $this->db->insert('location_items',$item_location_data); if($insert) { $this->db->reset(); //OR try $this->db->_reset_write(); to flush all traces of the query return $insert; } 

Решение 1:

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

INSERT IGNORE INTO ..(rest is same, just add ignore)..

Решение 2:

Если вы хотите перезаписать предыдущую строку с новой, вам необходимо выполнить следующий запрос:

INSERT INTO TABLE (f1,f2) VALUES ('f1','f2') ON DUPLICATE KEY UPDATE f1='f1',f2='f2'

Решение: 3

Измените свой первичный ключ, выполнив следующие запросы:

Создайте новое поле для первичного ключа:

ALTER TABLE tablename ADD new_primary_key BIGINT NOT NULL FIRST;

Отменить существующий первичный ключ:

ALTER TABLE tablename DROP PRIMARY KEY

Теперь создайте новое поле, созданное ранее как первичный ключ с автоматическим приращением

ALTER TABLE tablename MODIFY new_primary_key BIGINT AUTO_INCREMENT PRIMARY KEY

(это не повлияет на другие запросы в коде, вы можете сохранить их такими, какие есть, и просто добавить LIMIT 1 в операторы select)