2012-03-05 2 views
1

я в настоящее время есть две таблицыЕсть ли способ оптимизировать массив подзапросов в SQL select?

question 
-------- 
id 
title, character varying 

answer 
-------- 
id 
question_id 
votes, integer 

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

SELECT question.id, 
    question.title, 
    ARRAY(SELECT votes 
      FROM answer 
      WHERE answer.question_id = question.id) 
FROM question 
ORDER BY question.id 

Выход выглядит так:

id | title | ?column?      
----+----------+----------------------------------------------------- 
100 | How to | {5,2,7} 
101 | Where is | {0} 
102 | What is | {1} 

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

+1

Добавлен тег postgresql b/c, который выглядит как синтаксис, специфичный для postgres. Пожалуйста, не стесняйтесь удалять теги, если это не правильная оценка. – bernie

+0

Каков тип столбца 'vote' таблицы' answer'? Это массив или одно целое значение? Я предполагаю, что это одно значение «int». Ваш запрос не работает, если это массив. –

+0

Это целое число –

ответ

0

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

CREATE INDEX answer_question_id_idx ON answer(question_id); 

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

В качестве альтернативы, рассмотрите возможность использования соединения, как arc suggested. Я не эксперт в этом вопросе, но я думаю, что Postgres будет использовать хеш-соединение, а не несколько последовательных сканирований, делая запрос быстрее. Если вы хотите сохранить/название/формат массива ID, используйте array_agg:

SELECT question.id, question.title, array_agg(answer.votes) 
    FROM question 
    LEFT JOIN answer ON answer.question_id = question.id 
GROUP BY question.id, question.title 
ORDER BY question.id; 

Однако, есть один нюанс. Если вопрос не имеет ответа, вы получите фантастический вид результата:

id |  title  | array_agg 
----+-------------------+----------- 
    1 | How do I do this? | {3,5} 
    2 | How do I do that? | {NULL} 
(2 rows) 

Это происходит из-за LEFT JOIN, который создает значение NULL, когда ни одной строки из присоединяемой таблицы не доступны. С INNER JOIN вторая строка не появится вообще.

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

id |  title  | ?column? 
----+-------------------+---------- 
    1 | How do I do this? | {3,5} 
    2 | How do I do that? | {} 
+0

У меня уже есть этот индекс на месте, и это все еще занимает некоторое время. Я не вижу много улучшений в методе соединения после использования 'EXPLAIN'. –

2

Вы должны использовать присоединиться:

SELECT question.id, question.title, answer.votes 
FROM question 
JOIN answer ON answer.question_id == question.id 
ORDER BY question.id 

Если вы хотите, чтобы выходной столбец содержит каскадный список всех «голосов», связанные с вопросом, и вы на Postgres, проверить этот вопрос: How to concatenate strings of a string field in a PostgreSQL 'group by' query?

0

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

SELECT question.id, 
    question.title, 
    array_agg(answer.votes) as answer_votes 
FROM question 
JOIN answer ON answer.question_id = question.id 
GROUP BY question.id, question.title 
ORDER BY question.id 
Смежные вопросы