2012-01-27 1 views
13

У меня есть вид DB, который в основном состоит из двух SELECT запросов с UNION ALL, как это:Медленный запрос на «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 < ... 

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

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

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

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

И тот же ВЫБРАТЬ, с таким же WHERE условие, как выше работ ОК на них индивидуально.

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

(Просто говоря, это одна из последних версий Postgres.)

Edit: Добавление обезличенных планов запросов (Thaks в @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.

+1

столбец «время» на ваш взгляд не проиндексирован. Вам придется вручную индексировать этот столбец в своем представлении.Взгляните на план выполнения –

+0

Будет ли запрос против этого вида * всегда * ограничен временем? –

+0

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

ответ

-3

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

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

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

Теперь, по крайней мере, я не вижу какой-либо способ, чтобы он быстрее + ​​вид +, не материализовались

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

+0

Я сомневаюсь, что это имеет место здесь, так как из плана выполнения запроса 'v' вы можете видеть, что оба подзапроса ограничены полем' time' ('julia'), поэтому я почти уверен, что нет _huge temp table_ созданный, на который впоследствии применяется ограничение времени. –

+0

«Я почти уверен, что нет огромной таблицы temp» Возможно, вы правы, но администратор базы данных или тот, кто имеет точное представление о том, что происходит за сценой, может подтвердить это. Дайте ответ, ответ, который точно объяснит, почему запрос V занимает время. – bjan

+0

Oracle> = 8i с оптимизатором затрат (по умолчанию) обычно не делает то, что вы говорите. Он будет делать это, если оптимизатор считает, что это лучший/единственный вариант, но это происходит не очень часто. – gpeche

9

Это, кажется, случай ошибки пилота. План запроса «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 (*) from v» vs. «select * from v»), которая может иметь совершенно разные планы перед удалением соединения. Я думаю, что мы не достигнем очень далеко, если вы не опубликуете фактические запросы, представления и таблицы и используемые планы ...

+1

'v' действительно запрашивает от> 2 разных таблиц, как' v1' и 'v2' запрос из> 2 разных таблиц (для оценивая различные столбцы). Похоже, что эти столбцы не подвергаются сомнению при вытягивании из 'v1' и' v2' в отдельности, но при запросе 'v'. –

+0

см. Отредактированный ответ – maniek

+0

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

1

Возможно, возникнет вопрос о том, чтобы динамически генерировать новый SQL-код при каждом вызове вместо создания представления и интегрировать статью, где в каждом 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(..., ...); 
+2

Это не работает для меня, я определенно нуждаюсь в «одном взгляде, чтобы править ими всеми». :) –

5

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

(
    (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 < ...) 
2

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

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

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

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

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

1

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

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

+0

Не верил, что это поможет, пока на самом деле не попытается. Теперь, когда условие WHERE касается одного и того же столбца из той же таблицы, запрос выполняется намного быстрее. Спасибо за этот совет! –

0

Встречающиеся же сценарий на 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 < ...