【问题标题】:How to optimize this select如何优化此选择
【发布时间】:2019-03-29 19:13:07
【问题描述】:
SELECT *
FROM   trips,
       dates
WHERE  places_number_on_flag > 0
       AND places_number > 0
       AND ( places_number - ( (SELECT Count(id)
                                FROM   resrv_customers
                                WHERE  trip = trips.id
                                       AND ow > 0)
                               + (SELECT Count(id)
                                  FROM   resrv_customers
                                  WHERE  trip = trips.id
                                         AND extra_seat = 1
                                         AND ow > 0) ) < 20 )
       AND dates.id = trips.trip_date
       AND dates.from_date > 2458553;  

大约 1 分 50 秒后返回 5 行。

我希望它更快。

【问题讨论】:

  • 你看过执行计划了吗?
  • 请发布相关的CREATE TABLECREATE INDEX 声明以及计划。您还应该使用显式连接语法重新编写查询以获得更好的可读性。
  • @dodekja 。 . .您应该解释查询执行的逻辑。
  • Bad habits to kick : using old-style JOINs - 旧式 逗号分隔的表格列表 样式已替换为 ANSI 中的 proper ANSI JOIN 语法-92 SQL 标准(25 年前),不鼓励使用它

标签: sql postgresql optimization


【解决方案1】:

一种优化是只运行一次子查询并使用条件聚合来计算座位数:

SELECT *
FROM   trips
  JOIN dates on dates.id = trips.trip_date
WHERE  places_number_on_flag > 0
       AND places_number > 0
       AND ( places_number - ( (SELECT Count(id) + count(id) filter (where extra_seat = 1)
                                FROM   resrv_customers
                                WHERE  trip = trips.id
                                       AND ow > 0)) < 20 )
       AND dates.from_date > 2458553;  

请注意,我用显式 JOIN 运算符替换了 WHERE 子句中古老而脆弱的隐式连接。它对性能没有任何影响,只是更好的编码风格。

【讨论】:

  • 我要补充:使用变量名而不是“*”。请记住,Explain Analyze 将帮助您了解每一步的成本。
【解决方案2】:

我们可以尝试使用resrv_customers 表的连接来重写您的查询,而不是使用代价高昂的相关子查询:

WITH cte AS (
    SELECT
        trip,
        COUNT(CASE WHEN ow > 0 THEN 1 END) AS cnt1,
        COUNT(CASE WHEN extra_seat = 1 AND ow > 0 THEN 1 END) AS cnt2
    FROM resrv_customers
    GROUP BY trip
)

SELECT *
FROM trips t
INNER JOIN dates d
    ON t.trip_date = d.id
LEFT JOIN cte r
    ON r.trip = t.id
WHERE
    places_number > 0 AND
    places_number - (r.cnt1 + r.cnt2) < 20 AND
    d.from_date > 2458553;

这里的索引可能很棘手,因为您正在执行SELECT *。要让 Postgres 使用任何索引,您可能需要进行大量列覆盖,即创建大型索引。

【讨论】:

  • 只是对优化的评论,CTE 实际上可能会成为流程的瓶颈,因为当您只需要匹配trip.id 时,它需要对整个resrv_customers 表进行分组和计数。 ow &gt; 0 也可以在 where 子句中,因为两个计数都需要它,而且使用 filter 子句 (postgresql.org/docs/current/…) 通常比计数中的 case 快。
【解决方案3】:

试试这个:

with rc_count as
(
    select trip, count(*) as ow_count, sum( (extra_seat = 1)::int ) as ow_count_with_extra_seat
    from resrv_customers
    where ow > 0
    group by trip    
)

SELECT *
FROM   trips
join   dates on dates.id = trips.trip_date
join   rc_count rc on trips.id = trip 
WHERE  places_number_on_flag > 0
       AND places_number > 0
       AND dates.from_date > 2458553
       and places_number - (ow_count + ow_count_with_extra_seat) < 20       

【讨论】:

  • 注意:这个答案与我之前发布的几乎相同。
  • @TimBiegeleisen 没有注意到这一点。我专注于编写自己的答案,我还将共享条件 ow &gt; 0 移到 where 子句
猜你喜欢
  • 2016-04-03
  • 2020-06-21
  • 1970-01-01
  • 1970-01-01
  • 2012-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-01
相关资源
最近更新 更多