【问题标题】:SQL performance issues with inner selects in PostgreSQL for tabulated reportPostgreSQL 中用于表格报告的内部选择的 SQL 性能问题
【发布时间】:2020-02-20 06:26:19
【问题描述】:

使用 PostgreSQL 数据库:

我有一个调查应用程序,用户可以在其中输入活动并回答有关他们活动的问题。调查本身称为RECALLS_T,输入的事件是EVENTS_T,答案是ANSWERS_T。答案是针对提供的活动问题,存储在 ACTIVITY_QUESTIONS_T 中,由 Lookup (LOOKUP_T) 映射。

然后我需要运行一个基于事件的报告,其中每一行都是来自EVENTS_T 的每个召回 的事件(所有事件合并为所有召回)。但是,该报告中的某些列需要为某些答案指明一个值,否则这些单元格为 NULL。所以这是一个表格报告

示例(先是简单的平面内容,然后是复杂的表格内容):

RecallID | RecallDate | Event |..| WalkAlone | WalkWithPartner |..| ExerciseAtGym
256      | 10-01-19   | Exrcs |..| NULL      | NULL            |..| yes
256      | 10-01-19   | Walk  |..| yes       | NULL            |..| NULL
256      | 10-01-19   | Eat   |..| NULL      | NULL            |..| NULL
257      | 10-01-19   | Exrcs |..| NULL      | NULL            |..| yes

我的 SQL 对基于答案的列表列有内部选择,如下所示:

select 
-- Easy flat stuff first
r.id as recallid, r.recall_date as recalldate, ... ,

-- Example of Tabulated Columns:
(select l.description from answers_t ans, activity_questions_t aq, lookup_t l 
where l.id=aq.answer_choice_id and aq.question_id=13 
and aq.id=ans.activity_question_id and aq.activity_id=27 and ans.event_id=e.id) 
     as transportationotherintensity,
(select l.description from answers_t ans, activity_questions_t aq, lookup_t l
where l.id=66 and l.id=aq.answer_choice_id and aq.question_id=14
and aq.id=ans.activity_question_id and ans.event_id=e.id) 
     as commutework,
(select l.description from answers_t ans, activity_questions_t aq, lookup_t l
where l.id=67 and l.id=aq.answer_choice_id and aq.question_id=14 and aq.id=ans.activity_question_id and ans.event_id=e.id) 
     as commuteschool,
(select l.description from answers_t ans, activity_questions_t aq, lookup_t l
where l.id=95 and l.id=aq.answer_choice_id and aq.question_id=14 and aq.id=ans.activity_question_id and ans.event_id=e.id) 
     as dropoffpickup,

SQL 工作正常,报表被渲染,但性能很差。我证实它是成比例的糟糕:没有可以修复它的特定项目的灵丹妙药。每个内部选择都会导致糟糕的表现。 1000 行的结果集需要 15 秒,但应该不会超过 2-3 秒。

请注意,这些索引已经存在:

  • ANSWERS_T: 在ACTIVITY_QUESTION_ID, EVENT_ID
  • EVENTS_T: 在RECALL_ID
  • ACTIVITY_QUESTIONS_T:在ACTIVITY_IDQUESTION_IDANSWER_CHOICE_ID

这些内部选择是不是我做错了什么?

【问题讨论】:

    标签: sql postgresql hibernate jpa


    【解决方案1】:

    为了总结问题,您想使用条件聚合。在 PostgreSQL 中,您可以使用:

    select ans.event_id,
           max(l.description) filter (where aq.question_id = 13 and aq.activity_id = 27) as transportationotherintensity
           max(l.description) filter (where l.id = 66 and aq.question_id = 14 and aq.activity_id = 67) as commutework,
           . . .
    from activity_questions_t aq join
         lookup_t l
         on l.id = aq.answer_choice_id join
         answers_t ans
         on aq.id = ans.activity_question_id
    group by ans.event_id
    

    【讨论】:

    • 让我问一下,如果我用 Java 中的 1 行单个 Select 重写查询,用于在集合中累积数据的每一列,那会更快吗?我可以将这 40 个子选择替换为来自 Java 服务类的 40 个单行 DAO 提取。平仓会有收益吗?现在它们嵌套在一个带有 Order By 和所有其他从属子句的大选择中。
    • @geneb。 . .一般来说,我认为在数据库中运行单个查询将是最快的解决方案。但是,您可以比较您的数据和系统,看看哪个最适合您。
    • 我进一步研究了这个“过滤器”的答案,这似乎是一个黑客/解决方法。 “FILTER”用于具有条件的聚合函数,例如:“SUM (CASE WHEN ... THEN .. ELSE END”。这里我们使用“假”聚合函数来满足 FILTER,即“MAX (l.description)”,但我们对任何类型的最大值都不感兴趣。我们只希望返回列。我是否正确,这不是一个简单的改进,而是一个 hack?
    • @geneb。 . . .这是在 SQL 中表达逻辑的一种完全有效的方式。
    • 谢谢。你是对的,我的报告使用这种 FILTER 方法从 17 秒缩短到 1 秒。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-23
    • 1970-01-01
    相关资源
    最近更新 更多