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

时间:2017-12-05 16:35:56

标签: 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 ;

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

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

#1137 - Can't reopen table: 'src'

上述错误来自此查询

      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; 

这是我的表的http://sqlfiddle.com/#!9/bc5a01c数据 正如您所看到的,上面的查询将同一个表连接到src一次,再次连接为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 ;

但它始终返回SELECT CONCAT( 'Node ',vNodeID, ' missed.' );,因为它更新new_dijnodes_dst,即表格重复。

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

2 个答案:

答案 0 :(得分:1)

MySQL doc建议

  

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

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

  1. 将临时表替换为永久表
  2. 将临时表的后续调用替换为临时表
  3. 后面的子查询
  4. 重复临时表
  5. 查找解决方法自联接
  6. 由于你的特定问题是使用自联接更新而你不想要永久表,我建议制作重复的临时表是最合适的选择。

      

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

    你能否分享一下你在哪里遇到麻烦?

答案 1 :(得分:1)

添加另一个答案,因为评论时间过长。

在更新语句之后,我将临时表的两个实例都放在同一页面上,现在它正常工作。您可以看到输出here

请使用以下存储过程。

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; 

            DROP TEMPORARY TABLE IF EXISTS new_dijnodes; 
            CREATE TEMPORARY TABLE new_dijnodes AS SELECT * FROM new_dijnodes_dst;

              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 ;