如何使用一个SQL语句插入两个数据库表?

时间:2011-08-21 11:17:16

标签: mysql insert-update upsert mysql-insert-id

以下描述了两个数据库表。星号突出显示主键。

+----------------+    +----------------+
| 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 作为主键来管理(插入/更新)帖子

2 个答案:

答案 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以供参考。