【问题标题】:Why does this stored procedure return an empty set?为什么这个存储过程返回一个空集?
【发布时间】:2012-04-10 05:59:37
【问题描述】:

我正在尝试构建一个存储过程来封装一些复杂的逻辑。这是基本代码,稍微匿名:

SET TERM ^ ;

RECREATE PROCEDURE GET_DATA (
 USERID   INTEGER,
 W        INTEGER,
 X        INTEGER,
 Y        INTEGER)
RETURNS (
 ID       INTEGER,
 NAME     VARCHAR(64) CHARACTER SET UTF8)
AS 
BEGIN
  select first 1
    QP.ID,
    QO.NAME
  from QP
  join QO
    on QO.ID = QP.QO_ID
  where
    (QO.W = :w) and (QO.X = :x) and (QO.Y = :y)
    and ((QP.PREREQUISITE in (
      select VALUE
      from LOOKUP_TABLE1
      where USER_ID = :userid))
      or (QP.PREREQUISITE is null))
    and (QO.Q_ID not in (
      select VALUE
      from LOOKUP_TABLE2
      where USER_ID = :userid))
  order by QP.SEQUENCE desc
  into :ID, :NAME;
  suspend;
END^

SET TERM ; ^

预计返回 1 或 0 个结果。这在逻辑上是正确的;如果我使用SELECT 查询,手动替换参数,然后在 Firebird Maestro 中运行它,它会给出预期的结果。但是如果我说select ID, NAME from GET_DATA(1, 1, 2, 3),使用相同的参数,我会得到一个空的结果集。

所以在存储过程级别出了点问题。任何人都知道它是什么以及如何解决它?

【问题讨论】:

  • LOOKUP_TABLE2.VALUE 是可以为空的字段吗?因为如果<subquery> 的结果集包含NULL<value> NOT IN (<subquery>) 永远不会为真。 (如果这是问题所在,那么解决方法是将and VALUE is not null 添加到子查询中。)
  • @ruakh:不,两个查找表都有 NOT NULL 约束。就像我说的,这个查询在逻辑上是正确的;当我将它作为存储过程运行时,它只是没有运行任何结果。
  • @Mason 如果您为这些表和一些示例数据发布 DDL 声明会很好,以便我们重新创建它 - SP 似乎没问题
  • 您是否尝试在HopperIBExpert 中对其进行调试?
  • 这可能是转换问题吗?例:QO表的X、Y、W列是否也定义为INTEGER?您是否在 SP 上将变量更改为值,而不是在 Firebird Maestro 上尝试选择权?

标签: sql stored-procedures firebird


【解决方案1】:

即使选择返回 1 或 0 个结果,您的过程也始终返回 1 个结果,因为 SUSPEND 与选择无关。

要获取0或1来选择对应的结果,可以使用:

RECREATE PROCEDURE GET_DATA (
   USERID   INTEGER,
   W        INTEGER,
   X        INTEGER,
   Y        INTEGER)
  RETURNS (
   ID       INTEGER,
   NAME     VARCHAR(64) CHARACTER SET UTF8)
  AS
  BEGIN
    FOR select first 1
      QP.ID,
      QO.NAME
    from QP
    join QO
      on QO.ID = QP.QO_ID
    where
      (QO.W = :w) and (QO.X = :x) and (QO.Y = :y)
      and ((QP.PREREQUISITE in (
        select VALUE
        from LOOKUP_TABLE1
        where USER_ID = :userid))
        or (QP.PREREQUISITE is null))
      and (QO.Q_ID not in (
        select VALUE
        from LOOKUP_TABLE2
        where USER_ID = :userid))
    order by QP.SEQUENCE desc
    into :ID, :NAME DO
       suspend;
  END^

  SET TERM ; ^

【讨论】:

  • 1 或 0 结果 - 不错 :)
  • 但我并不总是得到 1 个结果;运行此查询时,我总是得到一个空集(0 个结果)。把它变成这样的 for 循环并没有帮助。
【解决方案2】:

SUSPEND 就像一个倒置的FETCH 意思是“发送一行作为结果”。

如果您使用 SUSPEND 添加 4 行; select * from GET_DATA(parameters) 的结果应该至少有四行带有空值。这意味着您的 select 语句没有返回任何行,或者有一行具有空值。

用更简单的查询做一些测试。

select 1, 'TEST' from RDB$DATABASE into :ID, :NAME;

【讨论】:

    【解决方案3】:

    您将其视为一个可选择的过程,但 Firebird 并不认为这是一个可选择的过程,而是一个可执行的过程。如果您在末尾添加SUSPEND,它会,AFAIK 应该可以解决您的问题。

    【讨论】:

    • 不。我试过了,它没有用。我会更新问题。
    • This page 暗示SUSPEND 实际上应该在之前 SELECT?
    • @ruakh SUSPEND“打印”返回变量,在选择之前它们是空的
    • @ruakh 不,它没有,你们仔细观察第一次暂停将返回在循环外初始化的值。
    • @MarkRotteveel:有趣的一点。我猜那个页面的作者只是糊涂了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多