Быстрее 'выбирают отличный thing_id, thing_name от table1' в оракуле

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

(function () {

  describe('foo', () => {
     it('bar', () => {
        expect(1+1).toEqual(2);
     });
  });

})();
6
задан Don Branson 2 June 2009 в 13:13
поделиться

11 ответов

[ПОСЛЕДНИЙ РЕДАКТИРОВАНИЕ]

Мой ОРИГИНАЛЬНЫЙ ОТВЕТ относительно создания соответствующего индекса на (имя, идентификатор) для замены индекса на (имя) приведен ниже. (Это не было ответом на исходный вопрос, который запрещал любые изменения базы данных.)

Вот утверждения, которые я не еще проверял. Вероятно, есть очевидная причина, по которой они не работают. Я бы никогда на самом деле не предлагал писать подобные утверждения (рискуя быть подвергнутым тщательной ругани за такое нелепое предложение).

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

 select m1.id
      , m2.name
   from (select min(t1.rowid) as min_rowid
              , t1.id
           from table1 t1
          where t1.id is not null
          group by t1.id
        ) m1
      , (select min(t2.rowid) as min_rowid
             , t2.name from table1 t2
         where t2.name is not null
         group by t2.name
        ) m2
  where m1.min_rowid = m2.min_rowid
  order
     by m1.id

Давайте распакуем это:

  • m1 - это встроенное представление, которое дает нам список различных значений идентификаторов.
  • m2 - это встроенное представление, которое предоставляет нам список различных значений имен.
  • материализует представления m1 и m2
  • соответствуют ROWID из m1 и m2 для сопоставления id с name

Кто-то еще предложил идею слияния индексов. Я ранее отвергал эту идею, план оптимизатора сопоставить 10 миллионов идентификаторов строк, не удаляя ни одного из них.

С достаточно низкой мощностью для идентификатора и имени и с правильным планом оптимизатора:

 select m1.id
      , ( select m2.name
            from table1 m2
           where m2.id = m1.id
             and rownum = 1
        ) as name
   from (select t1.id
           from table1 t1
          where t1.id is not null
          group by t1.id
        ) m1
  order
     by m1.id

Пусть ' s распаковать, что

  • m1 - это встроенное представление, которое дает нам список различных значений id.
  • материализовать представление m1
  • для каждой строки в m1 , запросить table1, чтобы получить значение имени из одной строки (стоп-ключ)

ВАЖНОЕ ПРИМЕЧАНИЕ

Эти операторы являются ФУНДАМЕНТАЛЬНО отличается от запроса OP. Они предназначены для возврата ОТЛИЧНОГО набора результатов от запроса OP. происходит для возврата желаемого набора результатов из-за причудливой гарантии данных. Дон сказал нам, что имя определяется id . (Верно ли обратное? Определяется ли id name ? Есть ли у нас ЗАЯВЛЕННАЯ ГАРАНТИЯ, не обязательно обеспечиваемая базой данных, но гарантия, которой мы можем воспользоваться?) любое значение ID , каждая строка с этим значением ID будет иметь одинаковое значение NAME . (Мы также гарантируем, что верно обратное, что для любого значения NAME каждая строка с этим значением NAME будет иметь одинаковое значение ID ?)

Если так, то, возможно, мы сможем использовать эту информацию. Если ID и NAME появляются в разных парах, нам нужно найти только одну конкретную строку. У «пары» будет соответствующий ROWID, который, как правило, доступен для каждого из существующих индексов. Что, если мы получим минимальный ROWID для каждого ID и получим минимальный ROWID для каждого NAME . Не мог t затем мы сопоставляем ID с NAME на основе ROWID, содержащего пару? Я думаю, что это могло бы сработать, учитывая достаточно низкую мощность. (То есть, если мы имеем дело только с сотнями ROWID, а не с десятками миллионов.)

[/ ПОСЛЕДНЕЕ ИЗМЕНЕНИЕ]

[РЕДАКТИРОВАТЬ]

Теперь вопрос обновлен информацией, касающейся таблицы, он показывает, что столбец ID и столбец ИМЯ допускают значения NULL. Если Дон может жить без каких-либо значений NULL, возвращаемых в результирующем наборе, то добавление предиката IS NOT NULL в оба этих столбца может позволить использовать индекс. (ПРИМЕЧАНИЕ: в индексе Oracle (B-Tree) значения NULL НЕ появляются в индексе.)

[/ EDIT]

ОРИГИНАЛЬНЫЙ ОТВЕТ:

создайте соответствующий индекс

create index table1_ix3 on table_1 (name,id) ... ;

Хорошо, это не ответ на вопрос, который вы задали , но это правильный ответ на устранение проблемы с производительностью. (Вы не указали никаких изменений в базе данных, но в этом случае изменение базы данных - правильный ответ.)

Обратите внимание, что если у вас есть индекс, определенный на (name, id) , то вы ( очень вероятно) не нужен индекс для (name) , так как оптимизатор будет учитывать ведущий столбец name в другом индексе.

(ОБНОВЛЕНИЕ: как кто-то более проницательный чем я указал, я даже не рассматривал возможность того, что существующие индексы были индексами растровых изображений, а не индексами B-дерева ...)


Пересмотрите свою потребность в наборе результатов ... вам нужно вернуть id , или будет достаточно возврата name .

select distinct name from table1 order by name;

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

select id from table1 where name = :b1 and rownum = 1;

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

select /*+ FIRST_ROWS */ DISTINCT id, name from table1 order by id;

,

select /*+ FIRST_ROWS */ id, name from table1 group by id, name order by name;

или

select /*+ INDEX(table1) */ id, min(name) from table1 group by id order by id;

ОБНОВЛЕНИЕ: как проницательно отметили другие, с помощью этого подхода мы тестируем и сравниваем производительность альтернативных запросов, что является своего рода подход ударил или промахнулся. (Я не согласен с тем, что он случайный, но я согласен, что это удачно или нет.)

ОБНОВЛЕНИЕ: Том предлагает подсказку ALL_ROWS. Я не подумал об этом, потому что я действительно был сосредоточен на получении плана запроса с использованием ИНДЕКСА. Я подозреваю, что запрос OP выполняет полное сканирование таблицы, и, вероятно, время занимает не сканирование, а уникальная операция сортировки (<10g) или операция хеширования (10gR2 +), которая требует времени. (Отсутствие хронометрированной статистики и трассировки события 10046, я просто предполагаю.) Но опять же, может быть, это сканирование, кто знает, отметка максимума на столе может быть выходом из огромного пространства пустых блоков.

Само собой разумеется, что статистика в таблице должна быть актуальной, и мы должны использовать SQL * Plus AUTOTRACE или хотя бы EXPLAIN PLAN для просмотра планов запросов.

Но ни один из предлагаемые альтернативные запросы действительно решают проблему производительности.

Возможно, подсказки повлияют на оптимизатор, чтобы выбрать другой план, в основном удовлетворяющий ORDER BY из индекса, но я ' Я не питаю на это особой надежды. (Я не думаю, что подсказка FIRST_ROWS работает с GROUP BY, подсказка INDEX может.) Я вижу потенциал для такого подхода в сценарии, где есть кучи пустых и редко заполненных блоков данных, и нет доступа к данным блоков через индекс, на самом деле может быть значительно меньше блоков данных, загруженных в память ... но этот сценарий был бы скорее исключением, чем нормой.


ОБНОВЛЕНИЕ: Как указывает Роб ван Вейк, с использованием средства трассировки Oracle - наиболее эффективный подход к выявлению и решению проблем с производительностью.

Без вывода EXPLAIN PLAN или SQL * Plus AUTOTRACE я просто предполагаю здесь.

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

Этого не избежать, запрос не может быть удовлетворен только с помощью индекса, поскольку нет индекса, который содержит столбцы NAME и ID , либо столбец ID или NAME в качестве ведущего столбца. Два других «быстрых» запроса OP могут быть удовлетворены из индекса без необходимости ссылаться на строку (блоки данных).

Даже если план оптимизатора для запроса заключался в использовании одного из индексов, он все равно должен получить связанную строку из блока данных, чтобы получить значение для другого столбца. А без предиката (без предложения WHERE) оптимизатор, скорее всего, выберет полное сканирование таблицы и, вероятно, выполнит операцию сортировки (<10g). (Опять же, EXPLAIN PLAN покажет план оптимизатора, как и AUTOTRACE.)

I ' m также здесь предполагается (большое предположение), что оба столбца определены как NOT NULL.

Вы также можете рассмотреть возможность определения таблицы как таблицы с индексированием (IOT), особенно если это единственные два столбца в таблице. (IOT - не панацея, у него есть собственный набор проблем с производительностью.)


Вы можете попробовать переписать запрос (если это не изменение базы данных, которое также запрещено). В наших средах баз данных мы рассматриваем запрос должен быть такой же частью базы данных, как таблицы и индексы.)

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

select /*+ INDEX(table1) */ ...
select /*+ FIRST_ROWS */ ...
select /*+ ALL_ROWS */ ...

  distinct id, name from table1;
  distinct id, name from table1 order by id;
  distinct id, name from table1 order by name;
  id, name from table1 group by id, name order by id;
  id, min(name) from table1 group by id order by id;
  min(id), name from table1 group by name order by name;

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

(ОБНОВЛЕНИЕ: кто-то еще указал, что оптимизатор может выбрать объединение двух индексов на основе ROWID. Это возможно, но без предиката для удаления некоторых строк это, вероятно, будет гораздо более дорогим подходом (сопоставление десятков миллионов ROWID) из двух индексов, особенно когда ни одна из строк не будет исключена на основе совпадения.)

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


Если не изменять что-либо еще в базе данных, единственная надежда (я могу придумать) на то, что вы ускорите запрос, - это убедиться, операция настроена таким образом, что (обязательная) операция сортировки может выполняться в памяти, а не на диске. Но это' s не совсем правильный ответ. Оптимизатор может вообще не выполнять операцию сортировки, вместо этого он может выполнять операцию хеширования (10gR2 +), и в этом случае ее следует настроить. Операция сортировки - это всего лишь предположение с моей стороны, основанное на прошлом опыте работы с Oracle 7.3, 8, 8i, 9i.)

У серьезного администратора баз данных будет больше проблем, если вы возьмете SORT_AREA_SIZE и / или HASH_AREA_SIZE параметров для ваших сеансов, чем он будет при создании правильных индексов. (И эти параметры сеанса являются «старой школой» для версий, предшествующих магии автоматического управления памятью 10g.)

Покажите вашему администратору базы данных спецификацию для набора результатов, пусть администратор базы данных настроит его.

13
ответ дан 8 December 2019 в 14:46
поделиться

Вы можете попробовать следующее:

select id, max(name) from table1 group by id

Это наверняка использует индекс по id, но вы должны попробовать, работает ли он быстро.

0
ответ дан 8 December 2019 в 14:46
поделиться

«Таблица очень большая (10 миллионов строк)» Если вы не можете изменить базу данных (добавить индекс и т. Д.). Тогда вашему запросу не останется ничего, кроме как прочитать всю таблицу. Итак, во-первых, определите, сколько времени это займет (т.е. время, указанное в SELECT ID, NAME FROM TABLE1). Вы не получите его быстрее, чем это. Второй шаг - ОТЛИЧИТЕЛЬНОСТЬ. В 10g + следует использовать HASH GROUP BY. До этого это операция СОРТИРОВКИ. Первый быстрее. Если ваша база данных - 9i, вы МОЖЕТЕ добиться улучшения, скопировав 10 миллионов строк в базу данных 10g и сделав это там. В качестве альтернативы можно выделить большие объемы памяти (Google ALTER SESSION SET SORT_AREA_SIZE). Это может нанести вред другим процессам в базе данных, но тогда ваши администраторы баз данных не предоставят вам особого выбора.

1
ответ дан 8 December 2019 в 14:46
поделиться

Уникальный идентификатор? Если это так, вы можете удалить DISTINCT из запроса. Если нет - может, ему нужно новое имя? Да, я знаю, схему нельзя изменить ...

0
ответ дан 8 December 2019 в 14:46
поделиться

Why do you need to even have "name" in the clause if the name is always the same for a given id? (nm...you want the name you aren't just checking for existence)

SELECT name, id FROM table WHERE id in (SELECT DISTINCT id FROM table)?

Don't know if that helps...

0
ответ дан 8 December 2019 в 14:46
поделиться

Without wishing to indulge in the practice of throwing stuff at the wall until something sticks, try this:

select id, name from table1 group by id, name

I have vague memories of a GROUP BY being inexplicably quicker than a DISTINCT.

0
ответ дан 8 December 2019 в 14:46
поделиться

Вы можете попробовать что-нибудь вроде

Select Distinct t1.id, t2.name
FROM (Select Distinct ID From Table) As T1
INNER JOIN table t2 on t1.id=t2.id

Select distinct t1.id, t2.name from table t1
inner Join table t2 on t1.id=t2.id

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

0
ответ дан 8 December 2019 в 14:46
поделиться

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

Есть среда разработки / база данных для тестирования этого материала?

Насколько своевременными должны быть данные?

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

Но если это не сработает ...

Как насчет экспорта всех пар идентификаторов и имен в альтернативную базу данных, где вы можете сгруппировать и индексировать в вашу пользу и оставить администраторов баз данных со всей их самодовольной жесткостью?

0
ответ дан 8 December 2019 в 14:46
поделиться

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

Вы, мы или оптимизатор должны знать статистику о ваших данных . А затем вы можете проводить измерения с помощью таких инструментов, как EXPLAIN PLAN или SQL Trace / tkprof, или даже простого инструмента автоматической трассировки из SQL Plus.

Не могли бы вы показать нам результат этого:

set serveroutput off
select /*+ gather_plan_statistics */ distinct id,name from table1;
select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));

И как ваши как выглядит вся таблица1? Пожалуйста, опишите результат.

С уважением, Роб.

2
ответ дан 8 December 2019 в 14:46
поделиться

Это может работать лучше. Предполагается, что, как вы сказали, имя всегда одно и то же для данного идентификатора.

WITH id_list AS (SELECT DISTINCT id FROM table1)
SELECT id_list.id, (SELECT name FROM table1 WHERE table1.id = id_list.id AND rownum = 1)
  FROM id_list;
0
ответ дан 8 December 2019 в 14:46
поделиться

Если для заданного id всегда возвращается одно и то же имя , вы можете выполнить следующее:

SELECT  (
        SELECT  name
        FROM    table1
        WHERE   id = did
                AND rownum = 1
        )
FROM    (
        SELECT  DISTINCT id AS did
        FROM    table1
        WHERE   id IS NOT NULL
        )

Оба запроса будут использовать индекс для id .

Если вам все еще нужны значения NULL , запустите следующее:

SELECT  (
        SELECT  name
        FROM    table1
        WHERE   id = did
                AND rownum = 1
        )
FROM    (
        SELECT  DISTINCT id AS did
        FROM    table1
        WHERE   id IS NOT NULL
        )
UNION   ALL
SELECT  NULL, name
FROM    table1
WHERE   id IS NULL
        AND rownum = 1

Это будет менее эффективно, так как второй запрос не использует индексы, но он будет остановитесь на первом NULL , с которым он сталкивается: если он находится близко к началу таблиц, то вам повезло.

См. запись в моем блоге для получения подробной информации о производительности:

]
0
ответ дан 8 December 2019 в 14:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: