Chr*_*nch 2 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 |
Run Code Online (Sandbox Code Playgroud)
和代码:
//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;
}
Run Code Online (Sandbox Code Playgroud)
在数据插入事务之前检查是否存在并不是一个好主意,因为这样可以平均地改变数据的可能性.你曾经看过这个错误但不容易重复的事实让我想知道这是否会发生.
建议将函数中第一个if块下面的代码更改为save生成以下SQL的代码:
INSERT INTO location_items (item_id, location_id)
VALUES ($ item_id ,$ location_id)
ON DUPLICATE KEY UPDATE
这包括存在检查以及在单个原子语句中插入或更新.(为了进一步说明如何实际实现它,我需要访问db代码.)
编辑:对不起,只是注意到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));
Run Code Online (Sandbox Code Playgroud)
(如果由于某种原因你不想这样做,保持原子性的另一种方法是将事件包装在事务中($this->db->trans_start();在存在检查之前和$this->db->trans_complete();插入/更新之后.但IMO会引入不必要的复杂性 - 个人更喜欢第一种方法.)
| 归档时间: |
|
| 查看次数: |
2129 次 |
| 最近记录: |