为什么查询中的“Where 1<> 1”会返回所有行?

时间:2009-03-19 17:31:54

标签: sql oracle views

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

Select *
From foo
where
    1 <> 1

当我解析它时,它应该什么也不返回(1 <> 1应该评估为false,对吧)。但是(至少在我的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(或select on foo中的某个列名)似乎说服Oracle给我回空集。这是一个长期解决方案,但不是短期解决方案(改变代码和重新部署是一个主要的PITA)。我希望数据库方面有一个鲜为人知的设置,或者视图中出现错误导致这种奇怪行为的原因。

15 个答案:

答案 0 :(得分:15)

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

答案 1 :(得分: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

答案 2 :(得分:5)

如果要动态生成WHERE子句。这样,您可以添加一些OR [another-condition]子句并使其工作,而不检查条件是否是第一个。

答案 3 :(得分:5)

它看起来像是Oracle优化器代码视图合并中的一个错误。我敢打赌你只能得到包含外连接的视图。您的ORDER BY解决了它,因为它实际上会强制NO_MERGE在视图上。

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

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

您还应该在Oracle支持下提出SR,因为这肯定是一个错误。无论您从中选择什么,或者内部有多复杂,该查询都不应该返回任何行。从来没有。

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

答案 4 :(得分:3)

为什么使用WHERE 1&lt;&gt; 1?

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

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

(除了我总是使用!=而不是&lt;&gt; - 我真的不应该这样做(见比尔的评论))

明显的Oracle Bug

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

答案 5 :(得分:3)

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

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

SELECT /*+ NO_MERGE */ ...

你还可以查看一个EXPLAIN PLAN来深入了解出了什么问题。

答案 6 :(得分:2)

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

答案 7 :(得分:2)

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

来确认这一点

SELECT 1 来自foo 在哪里1&lt;&gt; 1

如果1表中的第一列中的值已满,您可能希望坚持引用的整数:

SELECT * 来自FOO 在哪里'1'&lt;&gt; '1'

但是,在这里我可能完全错了。我没有方便的Oracle安装试用。 :P

答案 8 :(得分:1)

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

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

答案 9 :(得分:1)

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

Select *
From foo
where
    1 == 2

或可能

where
    key < 0

答案 10 :(得分:1)

也许您只想测试与数据库的连接。

答案 11 :(得分:1)

使用...

查看查询的执行计划会非常有趣
explain plan for select ...;

select * from table(dbms_xplan.display);

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

答案 12 :(得分:0)

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

答案 13 :(得分: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;

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

更新: 对字符串常量执行order by具有相同的效果,但不会改变计划(如解释计划所示)。我怀疑它会以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";

答案 14 :(得分:0)

与Cody Casterline的想法相关:视图中的第1列可能充满了NULL值吗?如果Oracle将WHERE解释为“第1列中的值不等于第1列中的值”并且第1列中的值为NULL,那么我们将进入SQL NULL的奇怪世界。我知道每个SQL方言都是NULL = NULL不是这样的。也许Oracle决定NULL&lt;&gt; NULL因此必须为真?