【问题标题】:Is there a way to optimise an array of subquery in a SQL select?有没有办法优化 SQL 选择中的子查询数组?
【发布时间】:2012-03-05 20:31:42
【问题描述】:

我目前有两张桌子

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 个答案。有没有办法优化以上内容?

【问题讨论】:

  • 添加了类似于 postgres 特定语法的 postgresql 标签 b/c。如果这不是正确的评估,请随时删除标签。
  • answervotes列的类型是什么?它是一个数组,还是一个整数值?我假设它是一个 int 值。如果它是一个数组,您的查询将不起作用。
  • 请添加查询的EXPLAIN ANALYZE

标签: sql postgresql query-optimization


【解决方案1】:

您应该使用连接:

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?

【讨论】:

    【解决方案2】:

    我建议在您的 answer 表上创建一个索引,并使用您的原始查询。

    CREATE INDEX answer_question_id_idx ON answer(question_id);
    

    如果没有此索引,它将不得不对整个表进行顺序扫描,以查找与 question_id 匹配的行。它必须对每个问题都这样做。

    或者,考虑使用连接,例如arc suggested。我不是这方面的专家,但我认为 Postgres 将使用散列连接而不是多次顺序扫描,从而使查询更快。如果要保留id/title/array格式,使用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? | {}
    

    【讨论】:

    • 我已经有了这个索引,但还需要一段时间。在使用EXPLAIN 之后,我没有看到加入方法有很多改进。
    【解决方案3】:

    如果您希望查询为每个问题生成一行,并将投票收集到一个数组中,您可以使用连接,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
    

    【讨论】:

      猜你喜欢
      • 2020-05-29
      • 1970-01-01
      • 1970-01-01
      • 2021-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多