Почему «Где 1 <> 1» в запросе возвращает все строки? - PullRequest
6 голосов
/ 19 марта 2009

Я столкнулся с запросом в приложении, которое я унаследовал, и выглядит так:

Select *
From foo
where
    1 <> 1

Когда я это анализирую, он ничего не должен возвращать (1 <> 1 должен иметь значение false, верно). Однако (по крайней мере, на моем компьютере с Oracle) возвращается полный список всего в foo. Когда я пытаюсь сделать то же самое в MSAccess / Jet и MSSQL, я получаю ожидаемое поведение. Чем он отличается от Oracle (и почему первоначальный разработчик хотел бы это сделать)?

Примечание: я видел какое-то суеверие о + s и -s использования «где 1 = 1», и это вызывало полное сканирование таблицы; но я не думаю, что это был первоначальный разработчик.

Небольшое обновление:
В этом случае 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). Я надеюсь, что есть некоторые известные настройки на стороне DB или что-то не так в View, что является причиной этого странного поведения.

Ответы [ 15 ]

15 голосов
/ 19 марта 2009

Хорошо ... почему это произойдет в Oracle, мне не под силу. Тем не менее, я могу рассказать вам, почему это часто используется в других БД: когда человек хочет, чтобы столбцы были возвращены, но без значений. (Например, для создания схемы для новой таблицы)

7 голосов
/ 19 марта 2009

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
5 голосов
/ 20 марта 2009

Это определенно похоже на ошибку в коде объединения представлений оптимизатора Oracle. Бьюсь об заклад, вы получите это только с представлениями, которые содержат внешние соединения. Ваш ORDER BY решает это, потому что он фактически заставляет NO_MERGE на виде.

Я бы не стал указывать подсказку ORDER BY или NO_MERGE внутри представления, потому что (в зависимости от объема ваших данных) это может ухудшить производительность других запросов, которые используют представление. Вы должны поместить подсказку no_merge во внешний запрос:

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

Вам также следует поднять SR с поддержкой Oracle, поскольку это определенно ошибка. Этот запрос никогда не должен возвращать какие-либо строки, независимо от того, из чего вы выбираете или насколько сложный внутри. Никогда и никогда.

Я не смог воспроизвести его, поэтому, вероятно, он исправлен в используемой версии. Какую версию БД вы используете?

5 голосов
/ 19 марта 2009

Если вы хотите динамически сгенерировать предложение WHERE. Таким образом, вы можете просто добавить несколько OR [another-condition] предложений и заставить их работать без проверки, является ли условие первым или нет.

3 голосов
/ 20 марта 2009

Похоже, ошибка в коде слияния представлений в Oracle. Oracle возьмет ваше предложение WHERE и объединит его с представлением SQL, а затем разработает план для этого.

Попробуйте выбрать с помощью этой подсказки и посмотрите, исчезнет ли проблема:

SELECT /*+ NO_MERGE */ ...

Вы также можете посмотреть на EXPLAIN PLAN, чтобы получить представление о том, что происходит не так.

3 голосов
/ 19 марта 2009

Зачем использовать ГДЕ 1 <> 1?

Единственное место, где я видел это, использовало или даже использовало его самостоятельно - это быстрый способ скопировать структуру таблицы без копирования содержимого:

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

(за исключением того, что я всегда использую! = Вместо <> - что мне действительно не следует (см. Комментарий Билла))

Кажущаяся ошибка Oracle

Если у вас есть случай, когда вы можете четко продемонстрировать, что Oracle возвращает строки в SQL Plus, когда вы выполняете команду «select * from my_view, где 1 <> 1», то вам следует обратиться в службу поддержки Oracle (или получить уполномоченного лица в вашей компании сделать так): это указало бы на значительную ошибку . Конечно, если вы используете старую версию Oracle, они, вероятно, просто скажут вам обновить!

2 голосов
/ 19 марта 2009

Просто мозговая атака здесь, и может быть совершенно неправильно, но я хочу сказать, что я видел, как некоторые парсеры SQL анализируют целые числа без кавычек как означающие «столбец X». Вы можете подтвердить это, попробовав:

ВЫБРАТЬ 1 ОТ ФУ ГДЕ 1 <> 1

Если 1 заполнен значениями из первого столбца вашей таблицы, вы, вероятно, захотите придерживаться целых чисел в кавычках:

ВЫБРАТЬ * ОТ ФУ ГДЕ '1' <> '1'

Но, опять же, я могу быть совершенно неправ здесь. У меня нет под рукой установки Oracle, чтобы попробовать ее. : Р

2 голосов
/ 19 марта 2009

Это будет звучать странно, но есть ли в представлении / таблице столбец с именем "1"?

1 голос
/ 20 марта 2009

Было бы очень интересно увидеть план выполнения запроса, используя ...

explain plan for select ...;

select * from table(dbms_xplan.display);

Если вы запрашиваете представление, то это может показать, как предикат оценивается в неправильной фазе

1 голос
/ 19 марта 2009

Возможно, если вы хотите просто проверить соединение с базой данных.

...