【问题标题】:Why temporary table is not allowed in stored procedure in Firebird?为什么 Firebird 的存储过程中不允许使用临时表?
【发布时间】:2016-03-02 13:28:25
【问题描述】:

我正在尝试在 Firebird 数据库的存储过程中创建临时表。

我的存储过程列表:

SET TERM ^ ;

CREATE PROCEDURE initNATIONALHEALTHFUNDS
 
AS BEGIN

  CREATE GLOBAL TEMPORARY TABLE temp_FUNDS 
  (
    NATIONALHEALTHFUNDID Integer NOT NULL,
    NAME Varchar(128) NOT NULL,
    CODE Integer NOT NULL
  )
  ON COMMIT PRESERVE ROWS;
  commit;
  
 INSERT INTO tempFUNDS (NATIONALHEALTHFUNDID, CODE, NAME)  VALUES ( 01 ,01 , 'Some Foundation');

    
  MERGE INTO NATIONALHEALTHFUNDS  AS target 
   USING tempFUNDS AS source 
   ON target.NATIONALHEALTHFUNDID = source.NATIONALHEALTHFUNDID
   WHEN NOT MATCHED THEN 
    INSERT (NATIONALHEALTHFUNDID, CODE, NAME) VALUES (source.NATIONALHEALTHFUNDID, source.CODE, source.NAME);
    
  drop  TABLE tempFUNDS;
END^

SET TERM ; ^

每次我尝试创建此程序时都会出错:

    Engine Code    : 335544569
Engine Message :
Dynamic SQL Error
SQL error code = -104
Token unknown - line 7, column 3
CREATE


Total execution time: 0.015s

我做错了什么?我正在使用 Firebird 3.0 RC

【问题讨论】:

    标签: database stored-procedures temp-tables firebird-3.0


    【解决方案1】:

    Firebird 不允许您在存储过程中使用 DDL,因此 PSQL 中不允许使用 CREATE 语句。如answer by lad2025 中所述,您可以使用EXECUTE STATEMENT 解决此限制。

    但是,global temporary table 背后的想法是您创建一次,它们会继续存在,以便以后使用。数据只对创建数据的连接可见,根据全局临时表的类型,在事务提交(ON COMMIT DELETE ROWS)或连接关闭(ON COMMIT PRESERVE ROWS)后删除数据。

    来自 Firebird 3.0 语言参考:

    全局临时表具有持久的元数据,但它们的内容 是事务绑定(默认)或连接绑定。每一个 事务或连接有自己的 GTT 私有实例, 与所有其他人隔离。仅在何时创建实例 GTT 被引用。它们在交易结束时被销毁或 断开连接时。

    因此,与其尝试在存储过程中创建全局临时表,不如先创建它,然后创建使用已定义的 GTT 的存储过程。

    【讨论】:

    • 这就是重点,经过一段时间的努力,我也认为这是最好的解决方案,谢谢!
    【解决方案2】:

    来自GTT documentation

    创建全局临时表

    是由引擎处理的常规 DDL 语句,其处理方式与处理 CREATE TABLE 语句的方式相同。 因此,它不是 可以在存储过程或触发器中创建或删除 GTT。

    您可以使用Dynamic-SQL 并使用EXECUTE STATEMENT 包装您的代码作为解决方法:

    SET TERM ^ ;
    
    CREATE PROCEDURE initNATIONALHEALTHFUNDS
    AS BEGIN
    
    EXECUTE STATEMENT
      'CREATE GLOBAL TEMPORARY TABLE temp_FUNDS 
      (
        NATIONALHEALTHFUNDID Integer NOT NULL,
        NAME Varchar(128) NOT NULL,
        CODE Integer NOT NULL
      )
      ON COMMIT PRESERVE ROWS;
      commit;';
    
     ...
    
    END^
    

    【讨论】:

    • 感谢您的提示,但是我收到错误“动态 SQL 错误 SQL 错误代码 = -204 表未知 TEMP_FUNDS”。 Firebird 中是否有替代临时表的方法来暂时保留临时行?
    • @robsonwk 您需要包装每个语句。不仅仅是创作。你这样做了吗?
    • @robsonwk 直接在存储过程中使用的对象需要在创建时存在。另请注意,在事务中创建的表不能在同一事务中的 DML 中使用。因此,如果您想使用这个执行语句解决方案,那么您需要使用动态 SQL 使用执行语句 with autonomous transaction 来创建 GTT 用于 DML。我建议你不要那样做。
    • @lad2025 是的,我尝试包装每条语句,但随后又出现另一个错误,我放弃以这种方式深入研究,我也无法弄清楚如何在我的插入语句中转义文字字符串.我选择了解决方案,一开始就创建一次gtt,无论如何,非常感谢!
    【解决方案3】:

    为了详细说明上述其他正确答案,我主要使用临时表来解决性能问题,例如当我有一个参数化的数据子集需要针对更大的集合进行查询时,例如:

    select * from MAIN_TABLE
       where MAIN_TABLE.ID in (select ID from GTT$IDS)
    

    其中 GTT$IDS 填充了 ID 的子集。

    有时,对于高度复杂的过程,我必须使用多个临时表,因此我在元数据中创建它们(当然是在 PSQL 语句之外),如下所示:

    create global temporary table GTT$IDS_1 (INT1 integer, INT2 integer);
    create index IDX_GTT$IDS_11 on GTT$IDS_1 (INT1);
    create index IDX_GTT$IDS_12 on GTT$IDS_1 (INT2);
    
    create global temporary table GTT$IDS_2
    ...
    
    create global temporary table GTT$IDS_3
    ...
    

    对于一些高级 SQL 人员来说,这样做可能过于简单,但对我来说是最有意义的(继承了我 dBase/VFP 时代的技术),并且与一堆复杂的连接相比,它的速度非常快。

    我从来没有真正花时间学习如何使用“计划”子句(或让它正常工作),所以基本上当我遇到慢查询时,我会使用这种技术通过代码生成计划,如果这有意义的话。

    【讨论】:

      猜你喜欢
      • 2021-07-18
      • 1970-01-01
      • 1970-01-01
      • 2015-02-15
      • 2014-12-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多