6

継承したアプリケーションで、次のようなクエリに遭遇しました。

Select *
From foo
where
    1 <> 1

私がそれを解析すると、何も返されない1 <> 1はずです( false と評価されるはずです)。ただし、(少なくとも私の Oracle ボックスでは) 内のすべての完全なリストが返されfooます。MSAccess/Jet と MSSQL で同じことを試すと、期待どおりの動作が得られます。Oracle ではなぜ違うのでしょうか (また、元の開発者がこれをやりたいと思ったのはなぜですか)?

注: "where 1 = 1" を使用する際の + と - についての迷信を見たことがありますが、それによってテーブル全体がスキャンされます。しかし、これは元の開発者が意図していたものではないと思います。

小さな更新:
この場合fooはビューです。実際のテーブルで同じことを試してみると、期待どおりの結果が得られます (行はありません)。

更新 2:
ウサギの穴のさらに下のコードをたどり、フィールド/列名を取得しようとしているだけだと判断しました。完全なレコードセットを返す理由については、まだ途方に暮れています。ただし、ビューのみ。

文字通り、彼はクエリを文字列で作成し、それを別の関数に渡して変更せずに実行しています。

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

この場合、strTableName にはビューの名前が含まれます。

更新 3:
参考までに、これは私が問題を抱えているビューの 1 つです (フィールド/テーブル/スキーマ名を変更しました)。

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のselectの列名)は、Oracleに空のセットを返すように説得するようです。これは長期的な解決策ですが、短期的な解決策ではありません (コードの変更と再デプロイは主要な PITA です)。DB 側にほとんど知られていない設定があるか、この奇妙な動作の原因であるビューに何か問題があることを願っています。

4

15 に答える 15

15

わかりました...なぜこれがオラクルで起こるのかは私にはわかりません。ただし、他の DB でよく使用される理由を説明できます。列を返したいが、値を返してほしくない場合です。(新しいテーブルのスキーマを作成する場合など)

于 2009-03-19T17:34:28.843 に答える
5

これは間違いなく、Oracle オプティマイザのビュー マージ コードのバグのようです。これは、外部結合を含むビューでのみ得られるに違いありません。あなたORDER BYはそれを解決します。なぜなら、それは実質的NO_MERGEにビューに a を強制するからです。

ただし、(データ量によっては) ビューを使用する他のクエリのパフォーマンスが低下する可能性があるため、ビュー内にORDER BYまたはヒントを入れません。NO_MERGE外側のクエリに no_merge ヒントを入れる必要があります。

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

これは間違いなくバグであるため、Oracle サポートに SR を提出する必要があります。そのクエリは、何を選択していても、内部がどれほど複雑であっても、行を返すことはありません。決して。

再現できなかったので、使用しているバージョンでは修正されている可能性があります。使用しているdbのバージョンは何ですか?

于 2009-03-20T12:08:57.497 に答える
5

WHERE節を動的に生成したい場合。OR [another-condition]このようにして、条件が最初のものであるかどうかを確認せずに、いくつかの句を追加して機能させることができます。

于 2009-03-19T17:34:34.117 に答える
3

Oracle のビュー結合コードのバグのように聞こえます。オラクルはあなたの WHERE 句を取り、それをビュー SQL にマージし、そのための計画を立てます。

このヒントを使用して選択を試し、問題が解決するかどうかを確認してください。

SELECT /*+ NO_MERGE */ ...

また、EXPLAIN PLAN を調べて、何がうまくいかないのかについての洞察を得ることができます。

于 2009-03-19T23:42:35.993 に答える
2

これは奇妙に聞こえるかもしれませんが、ビュー/テーブルには「1」という名前の列がありますか?

于 2009-03-19T18:13:38.547 に答える
2

ここでブレインストーミングを行っただけで、完全に間違っている可能性がありますが、引用符で囲まれていない整数を「列 X」を意味するものとして解析する SQL パーサーを見たことがあると言いたいです。これを確認するには、次を試してください。

SELECT 1 FROM foo WHERE 1 <> 1

1 がテーブルの最初の列の値でいっぱいの場合、おそらく引用符で囲まれた整数に固執する必要があります。

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

しかし、ここでも、私は完全に間違っている可能性があります。試してみるのに便利なOracleインストールがありません。:p

于 2009-03-19T18:45:12.153 に答える
1

...を使用して、クエリの実行計画を確認することは非常に興味深いでしょう。

explain plan for select ...;

select * from table(dbms_xplan.display);

ビューをクエリしている場合、これは、述語が間違ったフェーズでどのように評価されているかを示している可能性があります

于 2009-03-20T03:52:20.420 に答える
1

おそらく、データベースへの接続を単純にテストしたい場合です。

于 2009-03-19T17:50:07.463 に答える
0

SELECTおそらく壊れていません。まず、この動作を引き起こしている正確なクエリ文字列を取得します。VB 6アプリを介さずに、DBで直接クエリを実行します。問題はまだ発生していますか?そこから行きます。

于 2009-03-19T21:15:17.923 に答える
0

Cody Casterlineの考えに関連しています:ビューの列1はおそらくNULL値でいっぱいですか?OracleがWHEREを「列1の値が列1の値と等しくない」と解釈していて、列1の値がNULLである場合、SQLNULLの奇妙な世界に入ります。これは、NULL=NULLが真ではないことを私が知っているすべてのSQL方言に当てはまります。おそらく、OracleはNULL <> NULLが真でなければならないと判断したのでしょうか?

于 2009-03-19T22:04:59.193 に答える
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";
于 2009-03-19T21:45:54.887 に答える