6

我在继承的应用程序中遇到了一个查询,如下所示:

Select *
From foo
where
    1 <> 1

当我解析它时,它应该什么都不返回(1 <> 1应该评估为假,对)。但是(至少在我的 Oracle 机器上)它会返回一个完整的foo. 当我在 MSAccess/Jet 和 MSSQL 中尝试同样的事情时,我得到了我期望的行为。为什么 Oracle 会有所不同(以及为什么最初的开发人员想要这样做)?

注意:我已经看到一些关于使用“where 1 = 1”的 +s 和 -s 的迷信,它会导致全表扫描;但我认为这不是原始开发人员的意图。

小更新:
在这种情况下foo是一个视图。当我在一张实际的桌子上尝试同样的事情时,我得到了我所期望的(没有行)。

更新 2:
我已经在兔子洞中进一步跟踪代码,并确定他所做的只是试图获取字段/列名。我仍然不知道为什么它会返回完整的记录集。但仅限于意见。

从字面上看,他在字符串中构建查询并将其传递给另一个函数以原样执行。

'VB6
strSQL = "SELECT * FROM " & strTableName & " WHERE 1 <> 1"

在这种情况下,strTableName 包含视图的名称。

更新 3:
作为参考,这是我遇到问题的视图之一(我更改了字段/表/模式名称)

CREATE OR REPLACE FORCE VIEW scott.foo (field1,
                                        field2,
                                        field4,
                                        field5,
                                        field12,
                                        field8,
                                        field6,
                                        field7,
                                        field16,
                                        field11,
                                        field13,
                                        field14,
                                        field15,
                                        field17
                                       )
AS
   SELECT   bar.field1,
            bar.field2,
            DECODE
               (yadda.field9, NULL, 'N',
                DECODE (yadda.field3, NULL, 'Y', 'N')
               ) AS field4,
            bar.field5,
            snafu.field6,
            DECODE
                (snafu.field6,
                 NULL,
                bar.field8,
                   bar.field8
                 - snafu.field6
                ) AS field7,
            DECODE
               (yadda.field10,
                NULL,
            bar.field12,
                yadda.field10
               ) AS field11,
            DECODE
               (SIGN (  yadda.field10 - bar.field12),
                NULL, 'N', 1, 'N', 0, 'N', -1, 'Y'
               ) AS field13,
            bar.field14,
            ADD_MONTHS
               (DECODE (yadda.field10, NULL, bar.field12, yadda.field10
                       ),
                bar.field14 * 12
               ) AS field15,
       FROM clbuttic,
            bar,
            yadda,
            snafu
      WHERE clbuttic.asset_type = bar.asset_type
        AND bar.field16 = yadda.field9(+)
        AND bar.field1 = snafu.field1(+)
        AND (bar.field17 IS NULL)
   ;

附加Order By 1(或 foo 上的选择中的某些列名)似乎说服 Oracle 将空集还给我。这是一个长期解决方案,但不是短期解决方案(更改代码和重新部署是主要的 PITA)。我希望数据库端有一个鲜为人知的设置,或者视图中有问题,这是导致这种奇怪行为的原因。

4

15 回答 15

15

好的......为什么这会在 Oracle 中发生,这超出了我的理解。但是,我可以告诉你为什么它经常在其他数据库中使用:当人们想要返回列但没有值时。(例如为新表创建模式)

于 2009-03-19T17:34:28.843 回答
7

Oracle 不会为我这样做:

SQL*Plus: Release 10.2.0.1.0 - Production on Thu Mar 19 13:36:20 2009

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


Connected to:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

SQL> select * from wrkr where 1 <> 1;

no rows selected

SQL> select count(*) from wrkr;

  COUNT(*)
----------
        88

编辑:视图不是固有的,要么:

SQL> create view foo as select * from wrkr;

View created.

SQL> select count(*) from foo;

  COUNT(*)
----------
        88

SQL> select * from foo where 1 <> 1;

no rows selected
于 2009-03-19T17:38:22.187 回答
5

当您要动态生成WHERE子句时。这样,您可以只附加一些OR [another-condition]子句并使其工作,而无需检查条件是否是第一个条件。

于 2009-03-19T17:34:34.117 回答
5

它绝对看起来像是 Oracle 优化器的视图合并代码中的一个错误。我敢打赌,只有包含外部连接的视图才能获得此信息。你ORDER BY解决了它,因为它实际上强制了NO_MERGE视图。

不过,我不会在视图中放置一个ORDER BYNO_MERGE提示,因为(取决于您的数据量)它可能会降低使用该视图的其他查询的性能。您应该在外部查询中放置一个 no_merge 提示:

Select /*+ NO_MERGE(foo) */ *
From foo
where
    1 <> 1

您还应该在 Oracle 支持下提出 SR,因为这绝对是一个错误。无论您选择什么,或者里面有多复杂,该查询都不应该返回任何行。永远不能。

我无法重现它,所以它可能已在我使用的版本中修复。您使用的数据库版本是什么?

于 2009-03-20T12:08:57.497 回答
3

为什么使用 WHERE 1<>1?

我见过的一个地方使用它,甚至自己使用它是一种快速复制表格结构而不复制内容的方法:

create table foo2
as select * from foo where 1 <> 1;

(除了我总是使用 != 而不是 <> - 我真的不应该(见比尔的评论))

明显的甲骨文错误

如果您有一个案例,当您执行“select * from my_view where 1<>1”时,您可以清楚地证明 Oracle 在 SQL Plus 中返回行,那么您应该联系 Oracle 支持(或让您公司的授权人员这样做) ):这将表明存在重大错误。当然,如果您使用的是旧版本的 Oracle,他们可能只会告诉您升级!

于 2009-03-19T17:36:56.137 回答
3

听起来像是 Oracle 中视图合并代码中的错误。Oracle 会将您的 WHERE 子句合并到视图 SQL 中,然后为此制定计划。

使用此提示尝试您的选择,看看问题是否消失:

SELECT /*+ NO_MERGE */ ...

您还可以查看 EXPLAIN PLAN 以了解问题所在。

于 2009-03-19T23:42:35.993 回答
2

这听起来很奇怪,但是视图/表是否有一个名为“1”的列?

于 2009-03-19T18:13:38.547 回答
2

只是在这里集思广益,可能是完全错误的,但我想说我已经看到一些 SQL 解析器将未引用的整数解析为“列 X”。您可以通过尝试确认这一点:

从 foo 中选择 1,其中 1 <> 1

如果 1 充满了表第一列的值,您可能希望坚持使用带引号的整数:

SELECT * FROM WHERE '1' <> '1'

但是,再一次,我在这里可能完全错了。我没有方便的 Oracle 安装来试用它。:p

于 2009-03-19T18:45:12.153 回答
1

WHERE 1 = 1 应该导致全表扫描,就像WHERE完全省略子句一样。如果您要从表中检索每一行,那当然是全表扫描。

我无法评论WHERE 1 <> 1未能在 Oracle 上按预期工作。这听起来真的不对。您确定您从查询中看到了您描述的结果吗?再试一次以确定。

于 2009-03-19T17:37:45.497 回答
1

当您只想返回 SQL 表的所有列时,通常会使用类似的方法。如果它在 Oracle 中不起作用,您可能想尝试类似的错误,例如:

Select *
From foo
where
    1 == 2

或者可能

where
    key < 0
于 2009-03-19T17:48:20.043 回答
1

也许如果您想简单地测试与数据库的连接。

于 2009-03-19T17:50:07.463 回答
1

使用 ... 查看查询的执行计划将非常有趣

explain plan for select ...;

select * from table(dbms_xplan.display);

如果您正在查询视图,那么这可能会显示在错误阶段如何评估谓词

于 2009-03-20T03:52:20.420 回答
0

SELECT应该没有坏 首先,获取导致此行为的确切查询字符串。直接在数据库上运行查询,而不是通过 VB 6 应用程序。问题是否仍然存在?从那里去。

于 2009-03-19T21:15:17.923 回答
0

部分解决方案

通过附加order by 1以下where子句来更新视图。

WHERE clbuttic.asset_type = bar.asset_type
    AND bar.field16 = yadda.field9(+)
    AND bar.field1 = snafu.field1(+)
    AND (bar.field17 IS NULL)
order by 1;

它处理了症状(我不必重新编译和重新部署代码),但没有告诉我为什么会出现这种奇怪的行为。

更新:对字符串常量进行排序具有相同的效果,但不会改变计划(如解释计划所示)。我怀疑它会以 1 的顺序更快地执行(我认为应该按第一列排序)。

WHERE clbuttic.asset_type = bar.asset_type
    AND bar.field16 = yadda.field9(+)
    AND bar.field1 = snafu.field1(+)
    AND (bar.field17 IS NULL)
order by "a";
于 2009-03-19T21:45:54.887 回答
0

与 Cody Casterline 的想法有关:视图中的第 1 列是否可能充满了 NULL 值?如果 Oracle 将 WHERE 解释为“第 1 列中的值不等于第 1 列中的值”并且第 1 列中的值是 NULL,我们就进入了 SQL NULL 的奇怪世界。在我知道的每种 SQL 方言中都是这种情况,NULL=NULL 是不正确的。也许 Oracle 决定 NULL<>NULL 因此必须为真?

于 2009-03-19T22:04:59.193 回答