2013-12-14 3 views
0

У меня есть схемы в Postgress 9.2.0, который напоминает этоAdvice Как сделать этот запрос выполнить хорошо

CREATE TABLE emails 
(
    id serial NOT NULL, 
    subject text, 
    body text, 
    CONSTRAINT emails_pkey PRIMARY KEY (id) 
) 

CREATE TABLE email_participants 
(
    id serial NOT NULL, 
    kind text NOT NULL, 
    email_id integer NOT NULL, 
    CONSTRAINT email_participants_pkey PRIMARY KEY (id), 
    CONSTRAINT email_participants_email_id_fkey FOREIGN KEY (email_id) 
    REFERENCES emails (id) MATCH SIMPLE 
    ON UPDATE NO ACTION ON DELETE CASCADE 
) 

CREATE TABLE todos 
(
    id serial NOT NULL, 
    description text, 
    reference_email_id integer, 
    CONSTRAINT todos_pkey PRIMARY KEY (id), 
    CONSTRAINT todos_reference_email_id_fkey FOREIGN KEY (reference_email_id) 
    REFERENCES emails (id) MATCH SIMPLE 
    ON UPDATE NO ACTION ON DELETE CASCADE 
) 

CREATE INDEX todos_reference_email_id_index 
    ON todos 
    USING btree 
    (reference_email_id); 

CREATE TABLE calls 
(
    id serial NOT NULL, 
    description text, 
    reference_email_id integer, 
    CONSTRAINT calls_pkey PRIMARY KEY (id), 
    CONSTRAINT calls_reference_email_id_fkey FOREIGN KEY (reference_email_id) 
    REFERENCES emails (id) MATCH SIMPLE 
    ON UPDATE NO ACTION ON DELETE CASCADE 
) 

CREATE INDEX calls_reference_email_id_index 
    ON calls 
    USING btree 
    (reference_email_id); 

CREATE TABLE meetings 
(
    id serial NOT NULL, 
    description text, 
    reference_email_id integer, 
    CONSTRAINT meetings_pkey PRIMARY KEY (id), 
    CONSTRAINT meetings_reference_email_id_fkey FOREIGN KEY (reference_email_id) 
    REFERENCES emails (id) MATCH SIMPLE 
    ON UPDATE NO ACTION ON DELETE CASCADE 
) 

CREATE INDEX meetings_reference_email_id_index 
    ON meetings 
    USING btree 
    (reference_email_id); 

CREATE TABLE attachments 
(
    id serial NOT NULL, 
    description text, 
    reference_email_id integer, 
    CONSTRAINT attachments_pkey PRIMARY KEY (id), 
    CONSTRAINT attachments_reference_email_id_fkey FOREIGN KEY (reference_email_id) 
    REFERENCES emails (id) MATCH SIMPLE 
    ON UPDATE NO ACTION ON DELETE CASCADE 
) 

CREATE INDEX attachments_reference_email_id_index 
    ON attachments 
    USING btree 
    (reference_email_id); 

Все email_id колонны выше, имеют ограничения внешнего ключа на них.

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

Мне нужно, чтобы выбрать все письма и счета или идентификаторы любых из указанных строк в email_participants, несделанном, звонках, встречах, вложение

Таким образом, наиболее очевидная вещь, которая приходит на ум это внутренний присоединиться на email_participants и левого внешнего соединения в других таблицах:

SELECT * FROM "emails" e INNER JOIN "email_participants" ep 
ON ep.email_id = e.id 
LEFT JOIN TODOS t 
on e.id = t.reference_email_id 
LEFT JOIN Calls c 
on e.id = c.reference_email_id 
LEFT JOIN meetings m 
on e.id = m.reference_email_id 
LEFT JOIN Attachments at 
on e.id = at.reference_email_id 
WHERE ("user_id" = 1) 

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

"Hash Right Join (cost=51.11..68.16 rows=123 width=1047)" 
" Hash Cond: (t.reference_email_id = e.id)" 
" -> Seq Scan on todos t (cost=0.00..14.30 rows=430 width=157)" 
" -> Hash (cost=50.44..50.44 rows=53 width=890)" 
"  -> Nested Loop Left Join (cost=23.06..50.44 rows=53 width=890)" 
"    -> Nested Loop Left Join (cost=23.06..41.78 rows=15 width=797)" 
"     -> Nested Loop Left Join (cost=23.06..37.78 rows=7 width=645)" 
"       -> Hash Join (cost=23.06..35.58 rows=4 width=458)" 
"        Hash Cond: (e.id = ep.email_id)" 
"        -> Seq Scan on emails e (cost=0.00..11.80 rows=180 width=410)" 
"        -> Hash (cost=23.00..23.00 rows=5 width=48)" 
"          -> Seq Scan on email_participants ep (cost=0.00..23.00 rows=5 width=48)" 
"           Filter: (user_id = 1)" 
"       -> Index Scan using meetings_reference_email_id_index on meetings m (cost=0.00..0.53 rows=2 width=187)" 
"        Index Cond: (e.id = reference_email_id)" 
"     -> Index Scan using attachments_reference_email_id_index on attachments at (cost=0.00..0.55 rows=2 width=152)" 
"       Index Cond: (e.id = reference_email_id)" 
"    -> Index Scan using calls_reference_email_id_index on calls c (cost=0.00..0.55 rows=3 width=93)" 
"     Index Cond: (e.id = reference_email_id)" 

Этот sql должен быть наиболее эффективным, я могу это сделать, есть ли что-нибудь еще, что я могу сделать, чтобы сделать это быстрее или избежать всех этих левых объединений? Есть много таких таблиц объединения.

Будет ли создание представления сделать это лучше, и если да, может ли кто-нибудь дать совет по созданию такого вида?

+2

Какие индексы на месте? –

+0

Вы изучили план запроса? – 9000

+0

Положите объяснение перед ним, посмотрите, что он говорит. –

ответ

1

Если вы присоединяетесь к родительской записи к нескольким дочерним записям в разных таблицах, вы получаете проблему с 10 дочерними записями в таблице A и 20 дочерними записями в таблице B, производя 200 записей в конечном результате.

Вы, вероятно, лучше с числом, как это:

create view ... 
select ..., 
     (select count(*) from child_table_1 
         where foreign_key = parent_key) child_1_count, 
     (select count(*) from child_table_2 
         where foreign_key = parent_key) child_2_count, 
     ... 
from parent_table 
where user_id = 1 

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

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

(select array_to_string(array_agg(reference_email_id), ',') 
    from child_table_2 
    where foreign_key = parent_key) child_2_id_list, 
+0

Мне эта идея нравится, если мне нужны идентификаторы, а не подсчеты? – dagda1