php - Mysql错误: Duplicate entry '1-5' for key 'PRIMARY' on insert unsure of how

标签 php mysql sql codeigniter

我收到错误 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;
    }

最佳答案

在事务外插入数据之前检查是否存在不是一个好主意,因为这会留下数据同时更改的可能性。您曾经看到过此错误但它不容易重复这一事实让我想知道这是否可能发生过。

建议将 save 函数中第一个 if block 下的代码更改为生成以下 SQL 的内容: 插入 location_items (item_id, location_id) 值 ($item_id,$location_id) <强> ON DUPLICATE KEY UPDATE

这涵盖了单个原子语句中的存在性检查和插入或更新。 (为了进一步说明如何实际实现它,我需要访问 db 代码。)

编辑: 抱歉,刚刚注意到 db 代码是 CodeIgniter。我是这个框架的新手,但从简短的角度来看,上述方法看起来完全可行 here .像这样:

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

(如果出于某种原因您不想这样做,另一种保持原子性的方法是将语句包装在 transaction 中($this->db->trans_start() ; 在存在检查之前和 $this->db->trans_complete(); 在插入/更新之后。但是 IMO 这引入了不必要的复杂性 - 个人更喜欢第一种方法。)

关于php - Mysql错误: Duplicate entry '1-5' for key 'PRIMARY' on insert unsure of how,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24782271/

相关文章:

php - PDFTK 无法填写在 livecycle 中创建的 pdf 表单

php - 所有 POST 请求上的 Laravel 4 CSRF

php - Options指令禁止的目录索引

php - Html onclick 似乎在 echo PhP 中不起作用

mysql - 根据特定标准选择每个公司的产品最低价格

sql - 如何使用 Access SQL 在表中查找重复项?

php - 尝试更新表: Invalid parameter number: number of bound variables does not match number of tokens in

php - 如何使用 sql 数据填充 html 下拉列表

sql - 使用 CTE 简化 SQL 语句

sql - INSERT INTO 不会工作!