【问题标题】:SQL Performance Tuning for query with lots of table joins具有大量表连接的查询的 SQL 性能调优
【发布时间】:2021-08-19 15:04:48
【问题描述】:

我有一些 SQL,我一直在努力让它尽可能快。但是,因为我们有大量数据的表,SQL 需要很长时间才能运行。

更多信息:

x86_64-redhat-linux-gnu 上的 PostgreSQL 9.2.24,由 gcc (GCC) 编译 4.8.5 20150623(红帽 4.8.5-28),64 位

问题

我可以使用任何指针来加快速度吗?

SELECT DISTINCT
    trip.travel_order_num as order_num,
    min(contact.fname || ' ' || contact.sname) as traveller,
    min(agentcontact.fname || ' ' || agentcontact.sname) as agent,
    min(employee.staff_code) as staff_code,
    trip.projectnumber,
    min(lob.name) as cost_centre,

    trip.start_date,
    trip.end_date,
    DATE(trip.end_date) - DATE(trip.start_date) + 1 as number_of_days,
    DATE(trip.start_date) - DATE(reqcreationdate) as days_requested_in_advance,
    trip.created as tripcreationdate,
    trip.recoverablecost,
    tripstatus.name AS status,
    trip.company_id,
    trip.eventtypes,
    trip.trip_reason,
    min(to_char(timetofirstbooking.earliestbookingdate - trip.reqcreationdate, 'FMDD HH24:MI:SS')) as dates_taken_to_book,
    min(COALESCE(paidaccommodation.paid_accomtot, 0.00)) AS paid_accomtot,
    min(COALESCE(booked_accommodation_total, 0.00)) as booked_accommodation_total,
    min(COALESCE(null, 0.00)) AS paid_cartot,
    min(COALESCE(bookedcarhire.booked_car_hire_total, 0.00)) as booked_car_hire_total,
    min(COALESCE(paidflightfare.allstatuses_flighttot, 0.00)) AS allstatuses_flighttot,
    min(COALESCE(paidflightfare.savings, 0.00)) AS flightsavings_tot,
    min(COALESCE(paidflightfare.publishedfare, 0.00)) AS flightpublishedfare_tot,
    min(COALESCE(tripservicefeepaid.feeamount, 0.00)) as servicefee,
    min(COALESCE(tripchangeservicefeepaid.feeamount, 0.00)) as changeservicefee,
    min(COALESCE(otheritemspaid.amount, 0.00)) as otherfee,
    CASE
        WHEN trip.requester_personid IS NOT NULL THEN min(requestercontact.fname || ' ' || requestercontact.sname)
        WHEN trip.requester_personid IS NULL THEN min(agentcontact.fname || ' ' || agentcontact.sname)
    END as requester,
    CASE
        WHEN min(tas.lastname) IS NOT NULL AND min(tas.staffcode) IS NOT NULL THEN string_agg(DISTINCT(CONCAT(tas.firstname,' ',tas.lastname, ' (',tas.staffcode,')')),', ')
        WHEN min(tas.lastname) IS NOT NULL THEN string_agg(DISTINCT(CONCAT(tas.firstname,' ',tas.lastname)),', ')
        ELSE ''
    END as approver,
    min(bob.approvalRequestedDate) as approvalrequesteddate,
    max(bob.apprConcDate) as approvalconclusiondate,
    extract(EPOCH from min(timetoapprove))/3600 as timetoapprove


FROM TRIP
    LEFT JOIN TRIPTRAVELLERMAP ON triptravellermap.tripid = trip.trip_id
    LEFT JOIN FLIGHTFARE ON flightfare.triptravid = triptravellermap.triptravid
    LEFT JOIN TRAVELAGENT ON travelagent.agentid = trip.agent_id
    LEFT JOIN CONTACT as agentcontact ON agentcontact.member_id = travelagent.memberid
    LEFT JOIN ITEM ON item.itemid = trip.itemid
    LEFT JOIN LOB ON lob.lobid = item.lobid
    LEFT JOIN TRIPSTATUS ON tripstatus.statusid = trip.status
    LEFT JOIN PERSON_CONTACT ON person_contact.person_personid = triptravellermap.personid
    LEFT JOIN CONTACT ON contact.contact_id = person_contact.personaldetails_contact_id
    LEFT JOIN PERSON_EMPLOYEE ON person_employee.person_personid = triptravellermap.personid
    LEFT JOIN EMPLOYEE ON employee.employee_id = person_employee.employmentdetails_employee_id
    LEFT JOIN TRIPREQUIREMENTSMAPPING ON triprequirementsmapping.tripid = trip.trip_id
    LEFT JOIN TRIPREQUIREMENTS ON triprequirements.uri = triprequirementsmapping.corporatetravelrequesturi
    LEFT JOIN PERSON_CONTACT AS requesterperson ON requesterperson.person_personid = trip.requester_personid
    LEFT JOIN CONTACT AS requestercontact ON requestercontact.contact_id = requesterperson.personaldetails_contact_id
    LEFT JOIN trip_approver_snapshot tas ON trip.trip_id=tas.trip_id
    /* PAID ACCOMMODATION */
    LEFT JOIN (SELECT trip.trip_id, SUM(lineitems.amount) as paid_accomtot
        FROM TRIP
            INNER JOIN RESERVATION ON reservation.trip_id = trip.trip_id
            INNER JOIN ACCOMMODATION ON accommodation.reservationid = reservation.reservationid
            INNER JOIN LINEITEMS ON lineitems.invoiceid = accommodation.invoiceid
        WHERE accommodation.created >= '2021-05-01' and accommodation.created <= ('2021-05-23')
            AND accommodation.resstatus <> 2
        GROUP BY trip.trip_id) AS paidaccommodation ON paidaccommodation.trip_id = trip.trip_id

    /* BOOKED ACCOMMODATION */
    LEFT JOIN (SELECT trip.trip_id, SUM(accommodation.totalaftertax) as booked_accommodation_total
        FROM TRIP
            INNER JOIN RESERVATION ON reservation.trip_id = trip.trip_id
            INNER JOIN ACCOMMODATION ON accommodation.reservationid = reservation.reservationid
        WHERE accommodation.created >= '2021-05-01' AND accommodation.created <= '2021-05-23'
            AND accommodation.invoiceid IS NULL AND accommodation.resstatus <> 2
        GROUP BY trip.trip_id) AS bookedaccommodation ON bookedaccommodation.trip_id = trip.trip_id

    /* PAID CARRENTAL */
    LEFT JOIN (SELECT trip.trip_id, SUM(carrental.rental_price) as booked_car_hire_total
        FROM TRIP
            INNER JOIN RESERVATION ON reservation.trip_id = trip.trip_id
            INNER JOIN CARRENTAL ON carrental.reservationid = reservation.reservationid
        WHERE carrental.created >= '2021-05-01' AND carrental.created <= '2021-05-23' AND carrental.status <> 2
        GROUP BY trip.trip_id) AS bookedcarhire ON bookedcarhire.trip_id = trip.trip_id

    /* PAID FLIGHT DETAILS */
    LEFT JOIN ( SELECT trip.trip_id, SUM(flightfare.totalfare) as allstatuses_flighttot, SUM(flightfare.savings) as savings, SUM(flightfare.publishedfare) as publishedfare, MIN(flightfare.created) as earliestbooking
        FROM TRIP
            INNER JOIN TRIPTRAVELLERMAP ON triptravellermap.tripid = trip.trip_id
            INNER JOIN FLIGHTFARE ON flightfare.triptravid = triptravellermap.triptravid
        WHERE flightfare.created >= '2021-05-01' AND flightfare.created <= '2021-05-23'
            AND (flightfare.status <> 2 OR (flightfare.status = 2 AND flightfare.ticketed = 1))
            AND NOT(flightfare.ticketed = 0 AND flightfare.tickettimelimit < CURRENT_TIMESTAMP)
        GROUP BY trip.trip_id ) AS paidflightfare ON paidflightfare.trip_id = trip.trip_id

    /* TIME TO FIRST BOOKING */
    LEFT JOIN (SELECT cc.trip_id, MIN(earliest_booked_date) as earliestbookingdate
            FROM(
                (SELECT trip.trip_id, MIN(flightfare.created) as earliest_booked_date
                    FROM TRIP
                        INNER JOIN TRIPTRAVELLERMAP ON triptravellermap.tripid = trip.trip_id
                        INNER JOIN FLIGHTFARE ON flightfare.triptravid = triptravellermap.triptravid
                    GROUP BY trip.trip_id )

                UNION

                (SELECT trip.trip_id, MIN(carrental.created) as earliest_booked_date
                    FROM TRIP
                        INNER JOIN RESERVATION ON reservation.trip_id = trip.trip_id
                        INNER JOIN CARRENTAL ON carrental.reservationid = reservation.reservationid
                    GROUP BY trip.trip_id)

                UNION

                (SELECT trip.trip_id, MIN(accommodation.created) as earliest_booked_date
                    FROM TRIP
                        INNER JOIN RESERVATION ON reservation.trip_id = trip.trip_id
                        INNER JOIN ACCOMMODATION ON accommodation.reservationid = reservation.reservationid
                    GROUP BY trip.trip_id)
            ) as cc GROUP BY cc.trip_id) AS timetofirstbooking ON timetofirstbooking.trip_id = trip.trip_id

    /* TRIP FEES */
    LEFT JOIN (SELECT trip.trip_id, SUM(tripfee.feeamount + tripfee.feeamountvat) as feeamount
        FROM TRIP
            INNER JOIN TRIPFEE ON tripfee.tripid = trip.trip_id
        GROUP BY trip.trip_id) AS tripservicefeepaid ON tripservicefeepaid.trip_id = trip.trip_id

    /* TRIP CHANGE FEES */
    LEFT JOIN (SELECT trip.trip_id, SUM(tripfee.feeamount + tripfee.feeamountvat) as feeamount
        FROM TRIP
            INNER JOIN TRIPFEE ON tripfee.tripid = trip.trip_id
        WHERE tripfee.tripfeetype = 9
        GROUP BY trip.trip_id) AS tripchangeservicefeepaid ON tripchangeservicefeepaid.trip_id = trip.trip_id

    /* OTHER ITEMS PAID */
    LEFT JOIN (SELECT trip.trip_id, SUM(otheritems.amount + otheritems.vat) as amount
        FROM TRIP
            INNER JOIN OTHERITEMS ON otheritems.tripid = trip.trip_id
        GROUP BY trip.trip_id) AS otheritemspaid ON otheritemspaid.trip_id = trip.trip_id

    LEFT JOIN (
        SELECT requested,max(conclusiondate) as apprConcDate , max(requestedDate) as approvalRequestedDate, max(conclusiondate)- max(requestedDate) as timetoapprove
        FROM approvalsubmission
        left join triprequirements on approvalsubmission.requested=triprequirements.uri
        where approvalsubmission.conclusion='APPROVED'
        group by requested) as bob on bob.requested = triprequirements.uri

WHERE
    trip.trip_id IN (SELECT DISTINCT(trip_id) FROM reservation WHERE reservation.created >= ('2021-05-01') AND reservation.created <= ('2021-05-23'))
    --AND LOWER(employee.staff_code) LIKE LOWER('$P!{staffCode')

    AND trip.status <> 2
    AND trip.company_id = 6632
    --AND $P!{costCentreCompanySwitcher}

group by trip.trip_id, tripstatus.name
ORDER BY min(lob.name)

【问题讨论】:

  • 不确定postgresql,但一般EXISTS 往往比IN 快。对于其他优化,我们需要查看执行计划
  • @HoneyBadger,谢谢,我已将其更改为使用 EXISTS
  • 与您的问题无关,但是:Postgres 9.2 是no longer supported,您应该尽快计划升级。
  • 慢有多慢?有哪些指标?请read this。然后edit你的问题。您的查询似乎封装了应用程序业务规则的主要部分:它非常庞大。通常这种查询会在更小的块中逐步优化。
  • 为了帮助您的查询规划器,请避免使用此类语句。考虑将您的查询分解为一系列较小的查询,每个查询都创建结果的一个子集。收集中间结果表的统计信息。然后在最后一步将中间结果连接在一起。

标签: sql postgresql query-optimization postgresql-performance


【解决方案1】:

固定:

从 group by 子句中删除 status

max(tripstatus.name) AS status,
 ...
group by trip.trip_id

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-28
    • 2016-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-05
    相关资源
    最近更新 更多