Есть ли способ оптимизировать массив подзапроса в SQL-выбор? - PullRequest
1 голос
/ 06 марта 2012

У меня сейчас две таблицы

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 ответов. Есть ли способ оптимизировать вышесказанное?

Ответы [ 3 ]

2 голосов
/ 06 марта 2012

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

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

Если вы хотите, чтобы выходной столбец содержал объединенный список всех «голосов», связанных с вопросом, и вы находитесь на Postgres, проверьте этот вопрос: Как объединить строки строкового поля в PostgreSQL ' группировать по запросу?

0 голосов
/ 06 марта 2012

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

CREATE INDEX answer_question_id_idx ON answer(question_id);

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

В качестве альтернативы, рассмотрите возможность использования объединения, поскольку arc предлагает .Я не эксперт в этом вопросе, но я думаю, что Postgres будет использовать хеш-соединение, а не многократное последовательное сканирование, что сделает запрос быстрее.Если вы хотите сохранить формат идентификатора / заголовка / массива, используйте 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 голосов
/ 06 марта 2012

Если вы хотите, чтобы запрос производил по одной строке на вопрос, с голосами, собранными в массив, вы можете использовать объединение с 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
...