以下描述了两个数据库表。星号突出显示主键。
+----------------+ +----------------+
| posts | | url_references |
+----------------+ +----------------+
| id* | | url* |
| post_content | | post_id |
+----------------+ +----------------+
我希望 插入或更新 帖子 ,具体取决于表格中是否存在相应的条目 url_references 。达到此目的的最佳 SQL命令组合是什么?我想避免在 PHP 中处理有关插入或更新的决定。
以下方案使用PHP命令描述了替代的逐步行为。
SELECT * FROM url_references WHERE url = $url;
场景1:插入新条目。
// mysql_num_rows() returns 0
INSERT INTO post (post_content) VALUES ($postContent);
$postId = mysql_insert_id();
INSERT INTO url_references (url, post_id) VALUES ($url, $postId);
场景2:更新现有条目。
// mysql_num_rows() returns 1
$row = mysql_fetch_array($rows);
$postId = $row['post_id'];
UPDATE posts SET post_content = $postContent WHERE id = post_id;
修改1:请注意,我无法直接检查帖子中的 id !我想根据 url 作为主键来管理(插入/更新)帖子。
答案 0 :(得分:1)
如果您想以经典方式完成上述操作,请执行以下操作。
首先,我们可能同意url
是一个自然的主键。无论如何,您需要此列中的索引来加速查找:
CREATE UNIQUE INDEX url_references_idx ON url_references(url);
之后执行:
INSERT INTO url_references (url) VALUES ($url);
您最终会遇到两种情况:
•INSERT
成功。这意味着您的$url
是新的,您可以继续:
INSERT INTO posts (id, post_content) values (NULL, $postContent);
SELECT LAST_INSERT_ID(); // This will return $post_id
UPDATE url_references SET post_id = $post_id WHERE url = $url;
COMMIT;
在这种情况下,对url_references
中新插入的行的锁定保证另一个线程将成为第二个场景,如果第一个事务成功提交(或第一个场景,如果失败)。
•INSERT
失败。这意味着您的$url
已知,您可以继续:
SELECT post_id FROM url_references WHERE url = $url;
UPDATE posts SET post_content = $postContent WHERE id = $post_id;
COMMIT;
注意:如果您启用了正确的事务隔离级别INSERT
,则第一个url_references
语句可以保证autocommit=off
表上的竞争条件得到正确处理。
注意:在这种情况下使用SELECT ... FOR UPDATE
不起作用,因为它只会锁定现有行(我们需要锁定即将插入的不存在的行)。对不起,如果我对你感到困惑,请忽略我在你的问题上的评论。
答案 1 :(得分:0)
您可以使用REPLACE INTO
来避免在INSERT/UPDATE
表上的posts
之间做出决定,然后根据受REPLACE
影响的行数确定是否需要插入url_references。像这样:
$sql = "REPLACE INTO posts (post_id, post_content) VALUES ($postId, $postContent)";
$result = mysql_query($sql);
$numRows = mysql_num_rows($result);
if ($numRows == 1) {
// was INSERT not DELETE/INSERT (UPDATE) - need to insert into url_references
$postId = mysql_insert_id();
$sql = "INSERT INTO url_references (url, post_id) VALUES ($url, $postId)";
... do SQL INSERT etc
}
来自MySQL REPLACE
文档:
REPLACE语句返回一个指示行数的计数 影响。这是删除和插入的行的总和。如果 对于单行REPLACE,count为1,插入了一行而没有行 被删除了。如果计数大于1,则表示一行或多行 在插入新行之前删除了。
请注意,我没有测试过上面的伪代码,因此您可能需要对其进行调整。
请参阅:http://dev.mysql.com/doc/refman/5.0/en/replace.html以供参考。