3

我正在使用 PDO 调用以 DROP TABLE IF EXISTS 开头的存储过程。我随机得到 PDOException 'SQLSTATE[42S02]: Base table or view not found: 1146 Table 'historygr.reached' doesn't exist' 更烦人的是它会从告诉我到抛出异常说表已经存在,在几秒钟之内,似乎来自同一个连接。

我自己无法触发错误,但我会收到错误通知。

这是错误源自的 PHP:

$dbh = PDODB::getInstance();
$stmt = $dbh->query("CALL ListReached(".$this->item_id.")"); // <-- ERROR
$items = $stmt->fetchAll();

这是 MySQL 过程定义:

DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `ListReached`( IN root INT)
BEGIN
    DECLARE rows SMALLINT DEFAULT 0;

    DROP TABLE IF EXISTS reached;
    CREATE TABLE reached(
    node_id INT PRIMARY KEY
    ) ENGINE=HEAP;

    INSERT INTO reached VALUES (root);
    SET rows = ROW_COUNT();

    WHILE rows > 0 DO
    INSERT IGNORE INTO reached
        SELECT DISTINCT child_id 
        FROM related_item AS r
        INNER JOIN reached AS p ON r.parent_id = p.node_id;
    SET rows = ROW_COUNT();

    INSERT IGNORE INTO reached
        SELECT DISTINCT parent_id
        FROM related_item AS r
        INNER JOIN reached AS p ON r.child_id = p.node_id;
    SET rows = rows + ROW_COUNT();
    END WHILE;

    DELETE FROM reached WHERE node_id = root;

    SELECT * FROM reached;
    DROP TABLE reached;

END
4

1 回答 1

1

您正在达到比赛条件。

如果两个连接都在执行相同的脚本,它们将同时创建和删除相同的表,从而导致冲突。

考虑使用临时表而不是在事务中创建和删除真实表。

http://dev.mysql.com/doc/refman/5.1/en/create-table.html

创建表时可以使用 TEMPORARY 关键字。TEMPORARY 表仅对当前连接可见,并在连接关闭时自动删除。这意味着两个不同的连接可以使用相同的临时表名称,而不会相互冲突或与现有的同名非临时表冲突。(在删除临时表之前,现有表是隐藏的。)要创建临时表,您必须具有 CREATE TEMPORARY TABLES 特权。

编辑

正如评论中提到的,查询多次引用该表。

另一种方法是在过程中使用锁:http: //dev.mysql.com/doc/refman/5.5/en/miscellaneous-functions.html#function_get-lock

于 2013-01-02T16:28:30.463 回答