我经常遇到这样的问题,并且总觉得这不是最有效的逻辑,我似乎无法找到一个直接的答案:
如果需要根据某些条件检查数据库,并在匹配为true时更新记录,如果匹配为false则转到另一个语句,如果所有数据库匹配则最后插入新记录回来假,有没有一种更有效的方法来优化当前的分支?
$id = *first query to look for id* //returns null if no match
if (is_null($_id)) { #no match
$id = *fallback query looking for id *; -->returns null if no match
}
if (is_null($id)) {#still no match
$id = *last fallback query looking for id*; -->returns null if no match
}
if (is_null($id)) { # no match was found, doesn't exist in DB so insert it
//code to INSERT the new record
} else { #match was found
//code to update the record*
}
答案 0 :(得分:1)
您是否考虑过使用REPLACE INTO?
答案 1 :(得分:1)
最有效的方法是运行一个MySQL语句......
INSERT ... ON DUPLICATE KEY UPDATE
此方法可能无法满足您的使用案例,但是消除检查行存在的查询可以提高性能。只是尝试插入行,如果它违反了唯一的键约束,MySQL将拒绝它,不需要使用单独的SELECT语句进行检查。如果尝试插入行会引发一个"重复键"异常,然后执行UPDATE
部分中指定的分配。
此方法确实要求表除了AUTO_INCREMENT或系统指定的代理键之外至少有一个唯一约束。同样,您的用例可能不符合此要求,因此这种方法可能不合适。
另一方面说明:通过问题中概述的方法,并发系统中存在竞争条件的可能性。客户端运行SELECT以查找行,并在未找到时运行后续INSERT。第二个会话也可以查找同一行,但找不到它,并尝试插入它。
问题是数据库操作"检查一行并插入(如果它不存在)"不是原子的,因为有多次往返。
db session 1 - check if row exists, not found
db session 2 - check if row exists, not found
db session 1 - insert the row - succeed
db session 2 - insert the row - FAIL - because row already inserted
如果我们坚持运行单独的SELECT的方法来找出"匹配"如果存在行,那么我们至少可以尝试通过组合在单个语句中查找匹配行的查询来避免多次往返以找到该行;我们应该能够至少得到两个单独的SELECT语句和一个UNION ALL,我们可以返回一个鉴别器列来区分哪个查询返回了一行。