首页 > 解决方案 > 在外部数据库上更新时存储过程中出现令牌未知错误

问题描述

create or alter procedure UPDATECAS (
VNEWSTOCK double precision,
as
declare variable LCCOMANDO2 varchar(256);
begin
  LCCOMANDO2 = 'update CAS
          set STOCK = STOCK-' || :VNEWSTOCK || '';

  for execute statement LCCOMANDO2 on external 'C:\DB\DB.GDB' as
  user 'SYSDBA' password 'masterkey'
  suspend;
end

我正在使用 Firebird,我想创建一个存储过程来在另一个数据库中进行更新,但我不明白我缺少什么,因为编译时它给了我以下错误:

无法格式化消息 13:896 -- 找不到消息文件 C:\WINDOWS\firebird.msg。
动态 SQL 错误。
SQL 错误代码 = -104。
令牌未知。
暂停

标签: sqlstored-proceduressql-updatefirebird

解决方案


错误代码 13:896 通常显示为错误代码 336397184,它转换为消息“无效令牌”。(顺便说一句:“无法格式化消息”错误表明您正在使用fbclient.dll无法找到firebird.msg带有错误消息的文件,或者您正在使用不包含特定消息的旧版本)

在这种特定情况下,问题在于您犯了语法错误:您的语句缺少单词DO,如FOR EXECUTE STATEMENTFirebird 2.5 语言参考中所示。结果,Firebird 解析器SUSPEND在它不期望的位置找到 a (它要么期望,要么来自语法DO的另一个标记)。FOR EXECUTE STATEMENT- 明显但不正确的 - 修复将是:

for execute statement LCCOMANDO2 on external 'C:\DB\DB.GDB' as
  user 'SYSDBA' password 'masterkey' do
begin
  suspend;
end

注意:不需要将suspend;in begin...括起来,但我认为它可以提高可读性。end

这将解决直接问题,但随后会导致另一个错误,因为FOR EXECUTE STATEMENT它旨在执行生成结果集的语句,而UPDATE不是生成结果集。

相反,您需要使用EXECUTE STATEMENTwithout FOR. 我还强烈建议您适当地参数化更新语句,而不是将值连接到查询字符串中。鉴于您的存储过程不产生任何数据(它没有RETURNS子句),使用该SUSPEND子句也是不合适的。

最终的代码应该是这样的:

create or alter procedure UPDATECAS (VNEWSTOCK double precision)
as
begin
  execute statement ('update CAS set STOCK = STOCK - :newstock') (newstock = VNEWSTOCK) 
    on external 'C:\DB\DB.GDB' as user 'SYSDBA' password 'masterkey';
end

但请注意,对库存的东西使用双精度似乎并不合适。通常 stock 是离散单位,所以INTEGERorBIGINT会更合适,或者如果您需要十进制值,DECIMAL(or NUMERIC) 的准确性可能比DOUBLE PRECISION.


推荐阅读