Подтвердить что ты не робот

Медленный запрос в представлении "СОЮЗ ВСЕ"

У меня есть представление БД, которое в основном состоит из двух SELECT запросов с UNION ALL, например:

CREATE VIEW v AS
SELECT time, etc. FROM t1 // #1...
UNION ALL
SELECT time, etc. FROM t2 // #2...

Проблема в том, что выбор формы

SELECT ... FROM v WHERE time >= ... AND time < ...

выполнять на самом деле очень медленно.

Оба SELECT # 1 и # 2 довольно быстро, правильно проиндексированы и так далее: когда я создаю виды v1 и v2, например:

CREATE VIEW v1 AS
SELECT time, etc. FROM t1 // #1...

CREATE VIEW v2 AS
SELECT time, etc. FROM t2 // #2...

И тот же SELECT, с тем же условием WHERE, что и выше, работает нормально на них индивидуально.

Любые идеи о том, где может быть проблема и как ее решить?

(Как раз упомянуть, это одна из последних версий Postgres.)

Изменить: добавление анонимных планов запросов (thaks to @filiprem для ссылки на отличный инструмент):

v1:

Aggregate  (cost=9825.510..9825.520 rows=1 width=53) (actual time=59.995..59.995 rows=1 loops=1)
  ->  Index Scan using delta on echo alpha  (cost=0.000..9815.880 rows=3850 width=53) (actual time=0.039..53.418 rows=33122 loops=1)
          Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey))
          Filter: ((NOT victor) AND ((bravo_sierra five NULL) OR ((bravo_sierra)::golf <> 'india'::golf)))

v2:

Aggregate  (cost=15.470..15.480 rows=1 width=33) (actual time=0.231..0.231 rows=1 loops=1)
  ->  Index Scan using yankee on six charlie  (cost=0.000..15.220 rows=99 width=33) (actual time=0.035..0.186 rows=140 loops=1)
          Index Cond: (("juliet" >= 'seven'::uniform bravo oscar whiskey) AND ("juliet" <= 'november'::uniform bravo oscar whiskey))
          Filter: (NOT victor)

v:

Aggregate  (cost=47181.850..47181.860 rows=1 width=0) (actual time=37317.291..37317.291 rows=1 loops=1)
  ->  Append  (cost=42.170..47132.480 rows=3949 width=97) (actual time=1.277..37304.453 rows=33262 loops=1)
        ->  Nested Loop Left Join  (cost=42.170..47052.250 rows=3850 width=99) (actual time=1.275..37288.465 rows=33122 loops=1)
              ->  Hash Left Join  (cost=42.170..9910.990 rows=3850 width=115) (actual time=1.123..117.797 rows=33122 loops=1)
                      Hash Cond: ((alpha_seven.two)::golf = (quebec_three.two)::golf)
                    ->  Index Scan using delta on echo alpha_seven  (cost=0.000..9815.880 rows=3850 width=132) (actual time=0.038..77.866 rows=33122 loops=1)
                            Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey_two) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey_two))
                            Filter: ((NOT victor) AND ((bravo_sierra five NULL) OR ((bravo_sierra)::golf <> 'india'::golf)))
                    ->  Hash  (cost=30.410..30.410 rows=941 width=49) (actual time=1.068..1.068 rows=941 loops=1)
                            Buckets: 1024  Batches: 1  Memory Usage: 75kB
                          ->  Seq Scan on alpha_india quebec_three  (cost=0.000..30.410 rows=941 width=49) (actual time=0.010..0.486 rows=941 loops=1)
              ->  Index Scan using mike on hotel quebec_sierra  (cost=0.000..9.630 rows=1 width=24) (actual time=1.112..1.119 rows=1 loops=33122)
                      Index Cond: ((alpha_seven.zulu)::golf = (quebec_sierra.zulu)::golf)
        ->  Subquery Scan on "*SELECT* 2"  (cost=34.080..41.730 rows=99 width=38) (actual time=1.081..1.951 rows=140 loops=1)
              ->  Merge Right Join  (cost=34.080..40.740 rows=99 width=38) (actual time=1.080..1.872 rows=140 loops=1)
                      Merge Cond: ((quebec_three.two)::golf = (charlie.two)::golf)
                    ->  Index Scan using whiskey_golf on alpha_india quebec_three  (cost=0.000..174.220 rows=941 width=49) (actual time=0.017..0.122 rows=105 loops=1)
                    ->  Sort  (cost=18.500..18.750 rows=99 width=55) (actual time=0.915..0.952 rows=140 loops=1)
                            Sort Key: charlie.two
                            Sort Method:  quicksort  Memory: 44kB
                          ->  Index Scan using yankee on six charlie  (cost=0.000..15.220 rows=99 width=55) (actual time=0.022..0.175 rows=140 loops=1)
                                  Index Cond: (("juliet" >= 'seven'::uniform bravo_victor oscar whiskey_two) AND ("juliet" <= 'november'::uniform bravo_victor oscar whiskey_two))
                                  Filter: (NOT victor)

juliet time.

4b9b3361

Ответ 1

Это, кажется, случай ошибки пилот-сигнала. План запросов "v" выбирается из по меньшей мере 5 разных таблиц.

Теперь, вы уверены, что вы подключены к правой базе данных? Может быть, есть некоторые напуганные параметры поиска_папки? Может быть, t1 и t2 - фактически виды (возможно, в другой схеме)? Может быть, вы каким-то образом выбираете неправильный взгляд?

Отредактировано после разъяснения:

Вы используете совершенно новую функцию, называемую "удаление соединения": http://wiki.postgresql.org/wiki/What%27s_new_in_PostgreSQL_9.0#Join_Removal

http://rhaas.blogspot.com/2010/06/why-join-removal-is-cool.html

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

другое редактирование: Кажется, вы используете агрегат (например, "select count (*)" из v "vs." select * from v "), который может получить совершенно разные планы перед удалением соединения. Думаю, нам не удастся пройти очень далеко, если вы не опубликуете фактические запросы, представления и таблицы и используемые планы...

Ответ 2

Я считаю, что ваш запрос выполняется аналогично:

(
   ( SELECT time, etc. FROM t1 // #1... )
   UNION ALL
   ( SELECT time, etc. FROM t2 // #2... )
)
WHERE time >= ... AND time < ...

который оптимизатор испытывает трудности с оптимизацией. т.е. он делает UNION ALL сначала перед применением предложения WHERE, но вы хотите применить предложение WHERE до UNION ALL.

Не могли бы вы поместить предложение WHERE в CREATE VIEW?

CREATE VIEW v AS
( SELECT time, etc. FROM t1  WHERE time >= ... AND time < ... )
UNION ALL
( SELECT time, etc. FROM t2  WHERE time >= ... AND time < ... )

В противном случае, если представление не может иметь предложение WHERE, то, возможно, вы можете сохранить два представления и сделать UNION ALL с предложением WHERE, когда они вам понадобятся:

CREATE VIEW v1 AS
SELECT time, etc. FROM t1 // #1...

CREATE VIEW v2 AS
SELECT time, etc. FROM t2 // #2...

( SELECT * FROM v1 WHERE time >= ... AND time < ... )
UNION ALL
( SELECT * FROM v2 WHERE time >= ... AND time < ... )

Ответ 3

Я не знаю Postgres, но некоторые RMDB обрабатывают операторы сравнения хуже, чем BETWEEN в случае индексов. Я бы попытался использовать BETWEEN.

SELECT ... FROM v WHERE time BETWEEN ... AND ...

Ответ 4

Возможность состоит в том, чтобы динамически генерировать новый SQL-код для каждого вызова вместо создания представления и интегрировать предложение where в каждом SELECT запроса объединения

SELECT time, etc. FROM t1
    WHERE time >= ... AND time < ...
UNION ALL
SELECT time, etc. FROM t2
    WHERE time >= ... AND time < ...

EDIT:

Можете ли вы использовать параметризованную функцию?

CREATE OR REPLACE FUNCTION CallMyView(t1 date, t2 date)
RETURNS TABLE(d date, etc.)
AS $$
    BEGIN
        RETURN QUERY
            SELECT time, etc. FROM t1
                WHERE time >= t1 AND time < t2
            UNION ALL
            SELECT time, etc. FROM t2
                WHERE time >= t1 AND time < t2;
    END;
$$ LANGUAGE plpgsql;

Вызов

SELECT * FROM CallMyView(..., ...);

Ответ 5

Объедините две таблицы. Добавьте столбец для указания исходной таблицы. При необходимости замените исходные имена таблиц на представления, которые выбирают только соответствующую часть. Проблема решена!

Изучение шаблона дизайна для суперкласса/подкласса может быть вам полезным.

Ответ 6

Попробуйте создать свое представление, используя UNION DISTINCT вместо UNION ALL. Посмотрите, не дают ли он неправильных результатов. Посмотрите, дает ли она более высокую производительность.

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

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

Ответ 7

Обнаружен такой же сценарий на 11g:

Сценарий 1:

CREATE VIEW v AS
  SELECT time, etc. FROM t1 // #1...

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

SELECT ... FROM v WHERE time >= ... AND time < ...

Сценарий 2:

CREATE VIEW v AS
  SELECT time, etc. FROM t2 // #2...

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

SELECT ... FROM v WHERE time >= ... AND time < ...

Сценарий 3 с UNION ALL:

CREATE VIEW v AS
  SELECT time, etc. FROM t1 // #1...
  UNION ALL
  SELECT time, etc. FROM t2 // #2...

Следующие операции медленны. План разбивает t1 и t2 (которые также являются видами) и собирает их как большую серию союзов. Фильтры времени применяются правильно на отдельных компонентах, но они все еще очень медленные:

SELECT ... FROM v WHERE time >= ... AND time < ...

Я был бы рад просто получить время на стадионе t1 плюс t2, но это было более чем в два раза. Добавление подсказки parallel сделало трюк для меня в этом случае. Он переделал все в лучший план:

SELECT /*+ parallel */ ... FROM v WHERE time >= ... AND time < ...

Ответ 8

Я думаю, у меня нет большого количества баллов, чтобы опубликовать его в виде комментариев, поэтому я отправляю его как ответ

Я не знаю, как PostgreSQL работает за сценой, я думаю, вы можете получить ключ, если бы это был Oracle, так что вот как Oracle будет работать

Ваше представление UNION ALL медленнее, потому что за сценой записи из SELECT # 1 и # 2 объединяются во временной таблица, которая создается "на лету", а затем ваш SELECT... FROM v WHERE time > =... AND time <... выполняется в этой временной таблице. Поскольку индексируются как # 1, так и # 2, поэтому они работают быстрее по отдельности, как и ожидалось, но эта временная таблица не индексируется (конечно) и окончательные записи выбираются из этой временной таблицы, что приводит к более медленному отклику.

Теперь, по крайней мере, я не вижу возможности ускорить его + просмотр + не материализованный

Один из способов, кроме запуска SELECT # 1 и # 2, а UNION - явно, чтобы ускорить работу, было бы использовать хранимую процедуру или функцию в вашем язык программирования приложений (если это так), и в этой процедуре вы делаете отдельные вызовы для каждой индексированной таблицы, а затем объединяете результаты, которые не так просты, как SELECT... FROM v WHERE time > =... & Time <...: (