【问题标题】:ERROR 1137: Can't reopen table when joining a temporary tableERROR 1137:加入临时表时无法重新打开表
【发布时间】:2018-05-19 09:33:31
【问题描述】:

我有一个存储过程:

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,这是表重复。

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

【问题讨论】:

    标签: mysql


    【解决方案1】:

    MySQL doc 建议

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

    请参考this 线程。最实用的解决方案似乎是

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

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

    我试图创建另一个 new_dijnodes 但我无法让它工作

    您能分享一下您在这方面遇到的问题吗?

    【讨论】:

    • 非常感谢。我已经更新了我的问题,并为我的问题添加了表格复制方法,你能看看吗?
    【解决方案2】:

    添加另一个答案,因为评论太长了。

    在更新语句之后,我将两个临时表实例都放在了同一页面上,现在它工作正常。可以看到输出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 ;
    

    【讨论】:

    • 哇,非常感谢您的帮助,但我认为临时表的性能比其他的要好得多,但我发现它有所下降
    • 很可能是因为有一个没有索引的 3 个临时表连接。当你运行这个时,你的临时表的行数是多少?花很少的 CPU 时钟来提高性能可能是值得的。您可以像这样使用 SELECT 语句创建索引 CREATE TEMPORARY TABLE new_dijnodes_dst (INDEX index_name (column_name_1, column_name_2)) SELECT * FROM new_dijnodes;
    • pathid 和 nodeid 是主键,我想我必须在临时表中对它们进行索引,对吧?
    • 是的,您可能想要创建 2 个单独的索引,每个列一个。因为有一次,连接中只使用了时间列。
    • 嗯,非常感谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-20
    • 1970-01-01
    • 2016-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多