oracle 在仅更改字段、值时对两个相同的查询产生不同的结果

oracle produce different result for two same queries when only change field,value

我有 SQL 个查询。当我声明变量和 运行 查询时,它产生 39 行,但是当我使用文字值而不是变量和 运行 查询时,它产生三行。怎么可能?

这是带有变量的查询和结果

declare 
    fromnode number :=1;
    CURRENTESTIMATE number :=0;
begin
  for ts in (SELECT  e.fromnode,e.tonode,e.weight
      FROM TS_DIJNODEESTIMATE N 
      INNER JOIN TS_EDGE E ON N.ID=E.TONODE
      WHERE N.DONE=0 AND E.FROMNODE=fromnode  AND (CURRENTESTIMATE+E.WEIGHT)<N.ESTIMATE)
      loop
        dbms_output.put_line(ts.fromnode || ',' || ts.tonode || ',' || ts.weight);
      end loop;

end;

结果是

1,2,1306
1,5,2161
1,6,2661
2,3,919
2,4,629
3,2,919
3,4,435
3,5,1225
3,7,1983
4,2,629
4,3,435
5,3,1225
5,6,1483
5,7,1258
6,5,1483
6,7,1532
6,8,661
7,3,1983
7,5,1258
7,6,1532
7,9,2113
7,12,2161
8,6,661
8,9,1145
8,10,1613
9,7,2113
9,8,1145
9,10,725
9,11,383
9,12,1709
10,8,1613
10,9,725
10,11,338
11,9,383
11,10,338
11,12,2145
12,7,2161
12,9,1709
12,11,2145

用文字代替变量:

declare 
    fromnode number :=1;
    CURRENTESTIMATE number :=0;
begin
  for ts in (SELECT  e.fromnode,e.tonode,e.weight
      FROM TS_DIJNODEESTIMATE N 
      INNER JOIN TS_EDGE E ON N.ID=E.TONODE
      WHERE N.DONE=0 AND E.FROMNODE=1  AND (0+E.WEIGHT)<N.ESTIMATE)
      loop
        dbms_output.put_line(ts.fromnode || ',' || ts.tonode || ',' || ts.weight);
      end loop;

end;

结果是

1,2,1306
1,5,2161
1,6,2661

想要的结果是第二个结果。

您应该将变量的名称 fromnode 更改为 my_fromnode 或类似名称。

在您的第一个查询中,E.FROMNODE=fromnode 实际上是将列与 本身 和 returns true 进行比较。

问题在于命名:您为变量指定了与 table 列相同的名称:

E.FROMNODE=fromnode

Oracle 的范围规则使用最近的匹配来解析名称,从最里面的点开始,向外和向上工作。查询中最近的命名空间是 table 因此它首先尝试将 fromnode 解析为 table 列。这个名字很合适,所以编译器不会再看下去了。

实际上,您的 WHERE 子句过滤器在逻辑上与 1 = 1 相同,这就是您获得更多行的原因。这应该不足为奇,因为这是记录在案的行为。 PL/SQL 文档涵盖了变量标识符名称的范围 here and the interaction with variable names here。关键点是 Alex Poole 强调的一点:

"If a SQL statement references a name that belongs to both a column and either a local variable or formal parameter, then the column name takes precedence."

您认为通过将 fromnode 设为小写可以避免这种情况,但 PL/SQL 不区分大小写。正确的方法是使用命名约定,例如使用前缀标识变量:V 表示变量 - v_fromnode - 或 L 表示本地 - l_fromnode - 都是常见的约定。