Как сохранить две таблицы базы данных одним оператором SQL? - PullRequest
1 голос
/ 21 августа 2011

Ниже описаны две таблицы базы данных.Звездочкой выделены первичные ключи.

+----------------+    +----------------+
| posts          |    | url_references |
+----------------+    +----------------+
| id*            |    | url*           |
| post_content   |    | post_id        |
+----------------+    +----------------+

Я хочу вставить или обновить a сообщение на основеналичие соответствующей записи в таблице url_references .Какова лучшая комбинация команд SQL для достижения этой цели?Я бы хотел избегать обработки решения о insert или update в 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 ]

1 голос
/ 22 августа 2011

Если вы хотите сделать это классическим способом, выполните следующие действия.

Сначала мы можем согласиться, что 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 гарантирует, что другой поток перейдет во 2-й сценарий, если 1-я транзакция успешно зафиксирована (или 1-й сценарий, если она не удалась).

• Ошибка 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 в этом случае не работает, так как блокирует только существующие строки (и нам нужно заблокировать несуществующую строку, которая вот-вот будет вставлена). Извините, если я запутал вас, пожалуйста, проигнорируйте мой комментарий под вашим вопросом.

0 голосов
/ 21 августа 2011

Вы можете использовать REPLACE INTO, чтобы избежать выбора между INSERT/UPDATE в таблице posts, а затем, основываясь на количестве строк, затронутых REPLACE, определить, нужно ли вам вставить INRERT в 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 возвращает число, указывающее количество затронутых строк.Это сумма удаленных и вставленных строк.Если счетчик равен 1 для ЗАМЕНЫ одной строки, строка была вставлена ​​и строки не были удалены.Если число больше 1, одна или несколько старых строк были удалены перед вставкой новой строки.

Обратите внимание, что я не проверял вышеуказанный псевдокод, так что вы можетенужно настроить его.

См. http://dev.mysql.com/doc/refman/5.0/en/replace.html для справки.

...