1

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

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

4

2 回答 2

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在这种情况下 Using 不起作用,因为它只会锁定现有的行(我们需要锁定即将插入的不存在的行)。抱歉,如果我让您感到困惑,请忽略我在您问题下的评论。

于 2011-08-22T15:25:11.023 回答
0

您可以使用REPLACE INTO避免INSERT/UPDATEposts表之间做出决定,然后根据受影响的行数REPLACE确定是否需要INSERT into 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
}

来自 MySQLREPLACE文档:

REPLACE 语句返回一个计数以指示受影响的行数。这是删除和插入的行的总和。如果单行 REPLACE 的计数为 1,则插入了一行并且没有删除任何行。如果计数大于 1,则在插入新行之前删除了一个或多个旧行。

请注意,我没有测试上面的伪代码,所以你可能需要调整它。

请参阅:http ://dev.mysql.com/doc/refman/5.0/en/replace.html以供参考。

于 2011-08-21T11:30:17.917 回答