错误 1137:加入临时表时无法重新打开表

Maj*_*ati 4 mysql

我有一个存储过程:

DROP PROCEDURE IF EXISTS dijResolve; 
DELIMITER | 
CREATE PROCEDURE dijResolve( pFromNodeName VARCHAR(20), pToNodeName VARCHAR(20) ) 
BEGIN 
  DECLARE vFromNodeID, vToNodeID, vNodeID, vCost, vPathID INT; 
  CREATE TEMPORARY TABLE new_dijnodes engine=memory AS SELECT * FROM dijnodes;
  CREATE TEMPORARY TABLE new_dijpaths AS SELECT * FROM dijpaths;

  -- null out path info in the nodes table 
  UPDATE new_dijnodes SET PathID = NULL,Cost = NULL,Calculated = 0; 
  -- find nodeIDs referenced by input params 
  SET vFromNodeID = ( SELECT NodeID FROM new_dijnodes WHERE NodeName = pFromNodeName ); 
  IF vFromNodeID IS NULL THEN 
    SELECT CONCAT('From node name ', pFromNodeName, ' not found.' );  
  ELSE 
    BEGIN 
      -- start at src node 
      SET vNodeID = vFromNodeID; 
      SET vToNodeID = ( SELECT NodeID FROM new_dijnodes WHERE NodeName = pToNodeName ); 
      IF vToNodeID IS NULL THEN 
        SELECT CONCAT('From node name ', pToNodeName, ' not found.' ); 
      ELSE 
        BEGIN 
          -- calculate path costs till all are done 
          UPDATE new_dijnodes SET Cost=0 WHERE NodeID = vFromNodeID; 
          WHILE vNodeID IS NOT NULL DO 
            BEGIN 
              UPDATE  
                new_dijnodes AS src 
                JOIN new_dijpaths AS paths ON paths.FromNodeID = src.NodeID 
                JOIN new_dijnodes AS dest ON dest.NodeID = Paths.ToNodeID 
              SET dest.Cost = CASE 
                                WHEN dest.Cost IS NULL THEN src.Cost + Paths.Cost 
                                WHEN src.Cost + Paths.Cost < dest.Cost THEN src.Cost + Paths.Cost 
                                ELSE dest.Cost 
                              END, 
                  dest.PathID = Paths.PathID 
              WHERE  
                src.NodeID = vNodeID 
                AND (dest.Cost IS NULL OR src.Cost + Paths.Cost < dest.Cost) 
                AND dest.Calculated = 0; 

              UPDATE new_dijnodes SET Calculated = 1 WHERE NodeID = vNodeID; 

              SET vNodeID = ( SELECT nodeID FROM new_dijnodes 
                              WHERE Calculated = 0 AND Cost IS NOT NULL 
                              ORDER BY Cost LIMIT 1 
                            ); 
            END; 
          END WHILE; 
        END; 
      END IF; 
    END; 
  END IF; 
  IF EXISTS( SELECT 1 FROM new_dijnodes WHERE NodeID = vToNodeID AND Cost IS NULL ) THEN 
    -- problem,  cannot proceed 
    SELECT CONCAT( 'Node ',vNodeID, ' missed.' ); 
  ELSE 
    BEGIN 
      -- write itinerary to map table 
      DROP TEMPORARY TABLE IF EXISTS map; 
      CREATE TEMPORARY TABLE map ( 
        RowID INT PRIMARY KEY AUTO_INCREMENT, 
        FromNodeName VARCHAR(20), 
        ToNodeName VARCHAR(20), 
        Cost INT 
      ) ENGINE=MEMORY; 
      WHILE vFromNodeID <> vToNodeID DO 
        BEGIN 
          SELECT  
            src.NodeName,dest.NodeName,dest.Cost,dest.PathID 
            INTO vFromNodeName, vToNodeName, vCost, vPathID 
          FROM  
            new_dijnodes AS dest 
            JOIN new_dijpaths AS Paths ON Paths.PathID = dest.PathID 
            JOIN new_dijnodes AS src ON src.NodeID = Paths.FromNodeID 
          WHERE dest.NodeID = vToNodeID; 

          INSERT INTO Map(FromNodeName,ToNodeName,Cost) VALUES(vFromNodeName,vToNodeName,vCost); 

          SET vToNodeID = (SELECT FromNodeID FROM new_dijpaths WHERE PathID = vPathID); 
        END; 
      END WHILE; 
      SELECT FromNodeName,ToNodeName,Cost FROM Map ORDER BY RowID DESC; 
      DROP TEMPORARY TABLE Map; 
    END; 
  END IF; 
END; 
| 
DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

该功能取自该网站 http://www.artfulsoftware.com/infotree/qrytip.php?id=766

好吧,我已经将其更改为能够在临时表上进行计算,因此不需要在表中保存数据。但是我遇到了一个问题,Mysql不允许调用其他名称的临时表。所以在上面的代码中我会遇到一个错误,上面写着

#1137 - Can't reopen table: 'src'
Run Code Online (Sandbox Code Playgroud)

上述错误来自此查询

      UPDATE  
        new_dijnodes AS src 
        JOIN new_dijpaths AS paths ON paths.FromNodeID = src.NodeID 
        JOIN new_dijnodes AS dest ON dest.NodeID = Paths.ToNodeID 
      SET dest.Cost = CASE 
                        WHEN dest.Cost IS NULL THEN src.Cost + Paths.Cost 
                        WHEN src.Cost + Paths.Cost < dest.Cost THEN src.Cost + Paths.Cost 
                        ELSE dest.Cost 
                      END, 
          dest.PathID = Paths.PathID 
      WHERE  
        src.NodeID = vNodeID 
        AND (dest.Cost IS NULL OR src.Cost + Paths.Cost < dest.Cost) 
        AND dest.Calculated = 0; 
Run Code Online (Sandbox Code Playgroud)

这是我的表的http://sqlfiddle.com/#!9/bc5a01csrc数据 正如您所看到的,上面的查询一次又一次地连接同一个表dest并更新它们的字段。我尝试创建另一个 new_dijnodes,但无法使其工作,这是我的尝试

DROP PROCEDURE IF EXISTS dijResolve; 
DELIMITER | 
CREATE PROCEDURE dijResolve( pFromNodeName VARCHAR(20), pToNodeName VARCHAR(20) ) 
BEGIN 
  DECLARE vFromNodeID, vToNodeID, vNodeID, vCost, vPathID INT; 
  DECLARE vFromNodeName, vToNodeName VARCHAR(20); 
  DROP TEMPORARY TABLE IF EXISTS new_dijnodes; 
  DROP TEMPORARY TABLE IF EXISTS new_dijpaths; 

  CREATE TEMPORARY TABLE new_dijnodes engine=memory AS SELECT * FROM dijnodes;
  CREATE TEMPORARY TABLE new_dijpaths AS SELECT * FROM dijpaths;

  -- null out path info in the nodes table 
  UPDATE new_dijnodes SET PathID = NULL,Cost = NULL,Calculated = 0; 
  -- find nodeIDs referenced by input params 
  SET vFromNodeID = ( SELECT NodeID FROM new_dijnodes WHERE NodeName = pFromNodeName ); 
  IF vFromNodeID IS NULL THEN 
    SELECT CONCAT('From node name ', pFromNodeName, ' not found.' );  
  ELSE 
    BEGIN 
      -- start at src node 
      SET vNodeID = vFromNodeID; 
      SET vToNodeID = ( SELECT NodeID FROM new_dijnodes WHERE NodeName = pToNodeName ); 
      IF vToNodeID IS NULL THEN 
        SELECT CONCAT('From node name ', pToNodeName, ' not found.' ); 
      ELSE 
        BEGIN 
          -- calculate path costs till all are done 
          UPDATE new_dijnodes SET Cost=0 WHERE NodeID = vFromNodeID; 
          WHILE vNodeID IS NOT NULL DO 
            BEGIN 
            DROP TEMPORARY TABLE IF EXISTS new_dijnodes_dst; 
            CREATE TEMPORARY TABLE new_dijnodes_dst AS SELECT * FROM new_dijnodes;

              UPDATE  
                new_dijnodes  
                JOIN new_dijpaths  ON new_dijpaths.FromNodeID = new_dijnodes.NodeID 
                JOIN new_dijnodes_dst ON new_dijnodes_dst.NodeID = new_dijpaths.ToNodeID 
              SET new_dijnodes_dst.Cost = CASE 
                                WHEN new_dijnodes_dst.Cost IS NULL THEN new_dijnodes.Cost + new_dijpaths.Cost 
                                WHEN new_dijnodes.Cost + new_dijpaths.Cost < new_dijnodes_dst.Cost THEN new_dijnodes.Cost + new_dijpaths.Cost 
                                ELSE new_dijnodes_dst.Cost 
                              END, 
                  new_dijnodes_dst.PathID = new_dijpaths.PathID 
              WHERE  
                new_dijnodes.NodeID = vNodeID 
                AND (new_dijnodes_dst.Cost IS NULL OR new_dijnodes.Cost + new_dijpaths.Cost < new_dijnodes_dst.Cost) 
                AND new_dijnodes_dst.Calculated = 0; 

              UPDATE new_dijnodes SET Calculated = 1 WHERE NodeID = vNodeID; 

              SET vNodeID = ( SELECT nodeID FROM new_dijnodes 
                              WHERE Calculated = 0 AND Cost IS NOT NULL 
                              ORDER BY Cost LIMIT 1 
                            ); 
            END; 
          END WHILE; 
        END; 
      END IF; 
    END; 
  END IF; 
  IF EXISTS( SELECT 1 FROM new_dijnodes WHERE NodeID = vToNodeID AND Cost IS NULL ) THEN 
    -- problem,  cannot proceed 
    SELECT CONCAT( 'Node ',vNodeID, ' missed.' ); 
  ELSE 
    BEGIN 
      -- write itinerary to map table 
      DROP TEMPORARY TABLE IF EXISTS map; 
      CREATE TEMPORARY TABLE map ( 
        RowID INT PRIMARY KEY AUTO_INCREMENT, 
        FromNodeName VARCHAR(20), 
        ToNodeName VARCHAR(20), 
        Cost INT 
      ) ENGINE=MEMORY; 
      WHILE vFromNodeID <> vToNodeID DO 
        BEGIN 
        DROP TEMPORARY TABLE IF EXISTS new_dijnodes_src; 
        CREATE TEMPORARY TABLE new_dijnodes_src AS SELECT * FROM new_dijnodes;

          SELECT  
            new_dijnodes_src.NodeName,new_dijnodes.NodeName,new_dijnodes.Cost,new_dijnodes.PathID 
            INTO vFromNodeName, vToNodeName, vCost, vPathID 
          FROM  
            new_dijnodes 
            JOIN new_dijpaths  ON new_dijpaths.PathID = new_dijnodes.PathID 
            JOIN new_dijnodes_src  ON new_dijnodes_src.NodeID = new_dijpaths.FromNodeID 
          WHERE new_dijnodes.NodeID = vToNodeID; 

          INSERT INTO Map(FromNodeName,ToNodeName,Cost) VALUES(vFromNodeName,vToNodeName,vCost); 

          SET vToNodeID = (SELECT FromNodeID FROM new_dijpaths WHERE PathID = vPathID); 
        END; 
      END WHILE; 
      SELECT FromNodeName,ToNodeName,Cost FROM Map ORDER BY RowID DESC; 
      DROP TEMPORARY TABLE Map; 
    END; 
  END IF; 
END; 
| 
DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

但它总是 SELECT CONCAT( 'Node ',vNodeID, ' missed.' ); 在更新时返回new_dijnodes_dst返回,这是表重复。

我也无法制作任何真实的表格,因为这个过程对于每个用户来说都是唯一的,并且它的处理对于多用户来说并不容易。有什么解决方案可以解决这个问题?谢谢

小智 6

MySQL文档建议

您不能在同一查询中多次引用 TEMPORARY 表

请参考主题。最实用的解决方案似乎是

  1. 用永久表替换临时表
  2. 将后续对临时表的调用替换为临时表后面的子查询
  3. 重复的临时表
  4. 找到自加入的解决方法

由于您的特定问题是使用自联接进行更新并且您不需要永久表,因此我建议制作重复的临时表是最合适的选择。

我尝试创建另一个 new_dijnodes 但无法使其工作

您能分享一下您在哪里遇到麻烦吗?