Операторы Oracle <>,! =, ^ =

Я хочу знать разницу этих операторов, в основном их разницу в производительности.

Я просмотрел Разницу между < > и!= в SQL, он не имеет информации, связанной с производительностью.

Затем я нашел это на dba-oracle.com, это говорит о том, что в 10.2 и выше производительность может быть совсем иной.

Интересно, почему? != всегда работает лучше, чем <>?

ПРИМЕЧАНИЕ. Наши тесты и производительность в реальном времени показывают, что изменение от <> до != оказывает большое влияние на время возврата запросов. Я здесь, чтобы спросить, ПОЧЕМУ это происходит, а не то, такие же или нет. Я знаю их семантически, но на самом деле они разные.

Ответ 1

Я тестировал производительность различного синтаксиса для не равного оператора в Oracle. Я попытался устранить все внешние воздействия для теста.

Я использую базу данных 11.2.0.3. Никакие другие сеансы не подключены, и база данных была перезапущена до начала тестов.

Схема была создана с одной таблицей и последовательностью для первичного ключа

CREATE TABLE loadtest.load_test (
  id NUMBER NOT NULL,
  a VARCHAR2(1) NOT NULL,
  n NUMBER(2) NOT NULL,
  t TIMESTAMP NOT NULL
);

CREATE SEQUENCE loadtest.load_test_seq
START WITH 0
MINVALUE 0;

Таблица была проиндексирована для повышения производительности запроса.

ALTER TABLE loadtest.load_test
ADD CONSTRAINT pk_load_test
PRIMARY KEY (id)
USING INDEX;

CREATE INDEX loadtest.load_test_i1
ON loadtest.load_test (a, n);

Десять миллионов строк были добавлены в таблицу с использованием последовательности SYSDATE для временной метки и случайных данных через DBMS_RANDOM (A-Z) и (0-99) для двух других полей.

SELECT COUNT(*) FROM load_test;

COUNT(*)
----------
10000000

1 row selected.

Схема была проанализирована для обеспечения хорошей статистики.

EXEC DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'LOADTEST', estimate_percent => NULL, cascade => TRUE);

Три простых запроса: -

SELECT a, COUNT(*) FROM load_test WHERE n <> 5 GROUP BY a ORDER BY a;

SELECT a, COUNT(*) FROM load_test WHERE n != 5 GROUP BY a ORDER BY a;

SELECT a, COUNT(*) FROM load_test WHERE n ^= 5 GROUP BY a ORDER BY a;

Это точно то же самое, за исключением синтаксиса оператора не равно (не только < > и! =, но также ^ =)

Сначала каждый запрос запускается без сбора результата, чтобы исключить эффект кэширования.

Следующие тайминги и аутсорсинг были включены для сбора как фактического времени выполнения запроса, так и плана выполнения.

SET TIMING ON

SET AUTOTRACE TRACE

Теперь запросы запускаются поочередно. Первое - это & ​​lt; >

> SELECT a, COUNT(*) FROM load_test WHERE n <> 5 GROUP BY a ORDER BY a;

26 rows selected.

Elapsed: 00:00:02.12

Execution Plan
----------------------------------------------------------
Plan hash value: 2978325580

--------------------------------------------------------------------------------------
| Id  | Operation             | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |              |    26 |   130 |  6626   (9)| 00:01:20 |
|   1 |  SORT GROUP BY        |              |    26 |   130 |  6626   (9)| 00:01:20 |
|*  2 |   INDEX FAST FULL SCAN| LOAD_TEST_I1 |  9898K|    47M|  6132   (2)| 00:01:14 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("N"<>5)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      22376  consistent gets
      22353  physical reads
          0  redo size
        751  bytes sent via SQL*Net to client
        459  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         26  rows processed

Далее!=

> SELECT a, COUNT(*) FROM load_test WHERE n != 5 GROUP BY a ORDER BY a;

26 rows selected.

Elapsed: 00:00:02.13

Execution Plan
----------------------------------------------------------
Plan hash value: 2978325580

--------------------------------------------------------------------------------------
| Id  | Operation             | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |              |    26 |   130 |  6626   (9)| 00:01:20 |
|   1 |  SORT GROUP BY        |              |    26 |   130 |  6626   (9)| 00:01:20 |
|*  2 |   INDEX FAST FULL SCAN| LOAD_TEST_I1 |  9898K|    47M|  6132   (2)| 00:01:14 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("N"<>5)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      22376  consistent gets
      22353  physical reads
          0  redo size
        751  bytes sent via SQL*Net to client
        459  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         26  rows processed

Наконец, ^ =

> SELECT a, COUNT(*) FROM load_test WHERE n ^= 5 GROUP BY a ORDER BY a;

26 rows selected.

Elapsed: 00:00:02.10

Execution Plan
----------------------------------------------------------
Plan hash value: 2978325580

--------------------------------------------------------------------------------------
| Id  | Operation             | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |              |    26 |   130 |  6626   (9)| 00:01:20 |
|   1 |  SORT GROUP BY        |              |    26 |   130 |  6626   (9)| 00:01:20 |
|*  2 |   INDEX FAST FULL SCAN| LOAD_TEST_I1 |  9898K|    47M|  6132   (2)| 00:01:14 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("N"<>5)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      22376  consistent gets
      22353  physical reads
          0  redo size
        751  bytes sent via SQL*Net to client
        459  bytes received via SQL*Net from client
          3  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
         26  rows processed

План выполнения для трех запросов идентичен, а тайминги 2.12, 2.13 и 2.10 секунды.

Следует отметить, что в зависимости от того, какой синтаксис используется в запросе, в плане выполнения всегда отображается < >

Тесты повторялись десять раз для каждого синтаксиса оператора. Это тайминги: -

<>

2.09
2.13
2.12
2.10
2.07
2.09
2.10
2.13
2.13
2.10

!=

2.09
2.10
2.12
2.10
2.15
2.10
2.12
2.10
2.10
2.12

^=

2.09
2.16
2.10
2.09
2.07
2.16
2.12
2.12
2.09
2.07

Пока существует некоторая дисперсия нескольких сотых секунды, это не имеет значения. Результаты для каждого из трех вариантов синтаксиса одинаковы.

Выбор синтаксиса анализируется, оптимизируется и возвращается с одинаковыми усилиями в одно и то же время. Поэтому в этом тесте нет ощутимой выгоды от использования одного над другим.

"Ah BC", вы говорите: "В моих тестах я считаю, что есть реальная разница, и вы не можете доказать это иначе".

Да, я говорю, это совершенно верно. Вы не показывали свои тесты, запросы, данные или результаты. Поэтому мне нечего сказать о ваших результатах. Я показал, что при прочих равных условиях не имеет значения, какой синтаксис вы используете.

"Так почему я вижу, что лучше в моих тестах?"

Хороший вопрос. Есть несколько возможностей: -
  • Ваше тестирование неверно (вы не устранили внешние факторы - другая рабочая нагрузка, кеширование и т.д. Вы не указали информацию о которые мы можем принять обоснованным решением).
  • Ваш запрос - это особый случай (покажите мне запрос, и мы можем обсудить его).
  • Ваши данные - это особый случай (возможно, но как - мы тоже этого не видим).
  • Существует и другое внешнее влияние.

Я показал через документированный и повторяемый процесс, что нет никакой пользы для использования одного синтаксиса над другим. Я считаю, что < > != И ^ = являются синонимами.

Если вы считаете, что это не так, то

a) показать документированный пример, который я могу попробовать сам

и

b) используйте синтаксис, который вы считаете лучшим. Если я прав, и нет никакой разницы, это не имеет значения. Если вы правы, тогда остынь, у вас есть улучшение для очень небольшой работы.

"Но Берлсон сказал, что это лучше, и я доверяю ему больше, чем ты, Фарул, Льюис, Ките и все остальные бомжи".

Он сказал, что это лучше? Я так не думаю. Он не представил ни одного окончательного примера, теста или результата, а только связался с кем-то, говорящим, что!= Лучше, а затем процитировал некоторые из своих сообщений.

Показывать не говорят.

Ответ 2

Вы ссылаетесь на статью на сайте Burleson. Вы перешли по ссылке на архив Oracle-L? И прочитали ли вы другие письма, ответившие на письмо, которое Берлсон цитирует?

Я не думаю, что вы это сделали, иначе вы бы не задали этот вопрос. Потому что нет фундаментальной разницы между != и <>. Первоначальное наблюдение было почти наверняка случайностью, вызванной окружающими условиями в базе данных. Прочтите ответы Джонатана Льюиса и Стефана Фарута, чтобы понять больше.


"Уважение - это не то, что программист должен иметь, его основной отношение любого человека должно иметь"

До точки. Когда мы встречаем незнакомца на улице, то, конечно, мы должны быть вежливыми и относиться к ним с уважением.

Но если этот незнакомец хочет, чтобы я разработал свое приложение для базы данных определенным образом, чтобы "повысить производительность", тогда у них должно быть убедительное объяснение и некоторые пуленепробиваемые тестовые примеры, чтобы поддержать его. Отдельного анекдота от какого-то случайного человека недостаточно.

Ответ 3

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

Попробуйте выполнить запрос через план объяснения (или автотрассировать) и посмотрите что это говорит... В соответствии с этим, "! =" Считается таким же, как "< > " ... Джонатан Льюис

Джонатан Льюис - уважаемый эксперт в сообществе Oracle.

Просто из любопытства... Оптимизатор запросов генерирует другое план выполнения для двух запросов? С уважением, Крис

.

Может ли быть переменная привязки в действии? Определенный эффект write!= вместо < > заключается в принудительном повторном анализе. Если на первом выполнение значений для: id были разными, и если у вас есть гистограмма на claws_doc_id, это может быть причиной. И если вы скажете мне что claws_doc_id является основным ключом, тогда я спрошу вас, что такое цель подсчета, в частности, когда запрос в предложении EXISTS не коррелирует с внешним запросом и возвращает тот же результат что угодно: id. Похоже на запрос опроса. Код, окружающий его должно быть интересно.

Стефан Фарульт

.

Я уверен, что лексический синтаксический анализ преобразует либо: = в < > или < > в! =, но я не уверен, влияет ли это на текст sql соответствуют сохраненному контуру.

.

Планы объяснения выглядят одинаково? Те же затраты?

Следующий ответ от исходного плаката.

Джонатан, Спасибо за ваш ответ. Мы сделали план объяснения обе версии заявления, и они были идентичны, что и есть настолько озадачивает это. Согласно документации, эти две формы не равных одинаковы (вместе с ^ = и другими, что я не могу напечатать), поэтому мне не имеет смысла, почему есть какая-то разница в производительность.

Скотт Ханаан

.

Не все включено небольшое испытание, но оно появляется, по крайней мере, в 10.1.0.2 он попадает в "< > " для обоих (обратите внимание на строку фильтра для каждого план)

.

Есть ли у вас сохраненный план? Сохраненные контуры делают точный (буквальный) так что если у вас есть один сохраненный контур для, скажем, SQL с "! =", а для SQL не существует "< > " (или наоборот), сохраненный В контуре могут использоваться подсказки? (хотя, подумайте об этом, ваш ПЛАН EXPLAIN должен был показать подсказки, если выполнение хранимой схемы?)

.

Вы пробовали выйти за рамки просто объяснить и автотрассировать и запустить полный тракт 10046 уровня 12, чтобы увидеть, где тратится более медленная версия пора? Это может пролить свет на предмет, плюс - быть уверенным чтобы убедиться, что планы объяснения точно совпадают в 10046 файл трассировки (не те, которые были созданы с помощью опции EXPLAIN =), и в v $sqlplan. Есть некоторые "особенности" автотрассы и объясняют, что может заставить его не дать вам точный план объяснения.

С уважением, Брэндон

.

Является ли это явление полностью воспроизводимым?

Вы проверяли filter_predicates и access_predicates плана, или просто структура. Я не ожидаю никакой разницы, но предикатный порядок может привести к значительным изменениям в использовании ЦП, если вы не повезло.

Если нет никакой разницы, тогда включите статистику строк (изменить набор сеансов "_rowsource_execution_statistics" = true) и запустить запросов, затем возьмите план выполнения из V $sql_plan и присоединитесь к v $sql_plan_statistics, чтобы узнать, не указана ли какая-либо из цифр о last_starts, last_XXX_buffer_gets, last_disk_reads, last_elapsed_time дают вам подскажите, где время.

Если вы на 10gR2, есть /* + gather_plan_statistics */подскажите, что вы может использовать вместо "alter session".

С уважением Джонатан Льюис

В этот момент нить умирает, и мы не видим никаких дополнительных сообщений с оригинального плаката, что заставляет меня думать, что либо ОП обнаружил предположение, которое они сделали, это было неверно, либо не проводило дальнейшего расследования.

Я также укажу, что если вы выполните план объяснения или автотрассировку, вы увидите, что сравнение всегда отображается как <>.

Вот несколько тестовых кодов. Увеличьте количество итераций цикла, если хотите. Вы можете видеть, что одна или другая сторона получает большее число в зависимости от другой активности на сервере, но вы никоим образом не увидите, что один оператор выходит последовательно лучше другого.

DROP TABLE t1;
DROP TABLE t2;
CREATE TABLE t1 AS (SELECT level c1 FROM dual CONNECT BY level <=144000);
CREATE TABLE t2 AS (SELECT level c1 FROM dual CONNECT BY level <=144000);

SET SERVEROUTPUT ON FORMAT WRAPPED

DECLARE
   vStart  Date;
   vTotalA Number(10) := 0;
   vTotalB Number(10) := 0;
   vResult Number(10);
BEGIN   
   For vLoop In 1..10 Loop
      vStart := sysdate;
      For vLoop2 In 1..2000 Loop
         SELECT count(*) INTO vResult FROM t1 WHERE t1.c1 = 777 AND EXISTS
            (SELECT 1 FROM t2 WHERE t2.c1 <> 0);
      End Loop;
      vTotalA := vTotalA + ((sysdate - vStart)*24*60*60);

      vStart := sysdate;
      For vLoop2 In 1..2000 Loop
         SELECT count(*) INTO vResult FROM t1 WHERE t1.c1 = 777 AND EXISTS
            (SELECT 1 FROM t2 WHERE t2.c1 != 0);
      End Loop;
      vTotalB := vTotalB + ((sysdate - vStart)*24*60*60);

      DBMS_Output.Put_Line('Total <>: ' || RPAD(vTotalA,8) || '!=: ' || vTotalB);
      vTotalA := 0;
      vTotalB := 0;
   End Loop;

END;
DROP TABLE t1;
DROP TABLE t2;
CREATE TABLE t1 AS (SELECT level c1 FROM dual CONNECT BY level <=144000);
CREATE TABLE t2 AS (SELECT level c1 FROM dual CONNECT BY level <=144000);

SET SERVEROUTPUT ON FORMAT WRAPPED

DECLARE
   vStart  Date;
   vTotalA Number(10) := 0;
   vTotalB Number(10) := 0;
   vResult Number(10);
BEGIN   
   For vLoop In 1..10 Loop
      vStart := sysdate;
      For vLoop2 In 1..2000 Loop
         SELECT count(*) INTO vResult FROM t1 WHERE t1.c1 = 777 AND EXISTS
            (SELECT 1 FROM t2 WHERE t2.c1 <> 0);
      End Loop;
      vTotalA := vTotalA + ((sysdate - vStart)*24*60*60);

      vStart := sysdate;
      For vLoop2 In 1..2000 Loop
         SELECT count(*) INTO vResult FROM t1 WHERE t1.c1 = 777 AND EXISTS
            (SELECT 1 FROM t2 WHERE t2.c1 != 0);
      End Loop;
      vTotalB := vTotalB + ((sysdate - vStart)*24*60*60);

      DBMS_Output.Put_Line('Total <>: ' || RPAD(vTotalA,8) || '!=: ' || vTotalB);
      vTotalA := 0;
      vTotalB := 0;
   End Loop;

END;

Ответ 4

Программист будет использовать !=

DBA будет использовать <>

Если существует другой план выполнения, может быть, что для каждой нотации существуют различия в кеше запросов или статистике. Но я действительно не думаю, что это так.

Edit:

Что я имею в виду выше. В сложных базах данных могут быть некоторые странные побочные эффекты. Я не знаю, оракул достаточно хорош, но я думаю, что есть кэш компиляции запросов, как в SQL Server 2008 R2. Если запрос компилируется как новый запрос, оптимизатор базы данных вычисляет новый план выполнения в зависимости от текущей статистики. Если статистика изменится, это приведет к другому, может быть, худшему плану.