简体   繁体   中英

How to improve performance of multiple joined table in sql query

How do I improve performance this query while also get all the information needed..

SELECT 
    tr.id, tr.request_status, tr.note, tr.created_date, 
    c.name AS customer_name, c.mobile_phONe, 
    u.full_name AS created_by_name, tt.name AS ticket_type_name 
FROM  
    ticket_request tr 
LEFT JOIN 
    ticket_type tt ON tt.id = tr.ticket_type_id 
LEFT JOIN 
    users u ON u.id = tr.created_by 
LEFT JOIN 
    customer c ON c.id = tr.customer_id 
WHERE 
    tr.is_deleted != 1 
    AND tr.user_id IN (SELECT u.id FROM users u WHERE u.status = '1') 
GROUP BY 
    tr.id  
ORDER BY 
    tr.created_date DESC 
LIMIT 0,20 

Currently, this query runs in 7-10 seconds.

  • ticket_request table has about 100k rows
  • customers table has about 300k rows
  • users table and ticket_type don't have that much (about 1k rows)

The speedup technique below is to dispense with the LIMIT first , and only after that, do all the JOINs .

SELECT  tr3.id, tr3.request_status, tr3.note, tr3.created_date,
        c.name AS customer_name, c.mobile_phONe,
        u2.full_name AS created_by_name,
        tt.name AS ticket_type_name
    FROM  
    (
        SELECT  tr1.id
            FROM  ticket_request tr1
            JOIN  users u1  ON u1.id = tr1.created_by
            WHERE  u1.status = '1'
              AND  tr1.is_deleted != 1
            ORDER BY  tr1.created_date DESC
            LIMIT  0,20 
    ) AS tr2
    JOIN  ticket_request AS tr3  ON tr3.id = tr2.id
    JOIN  user AS u2 ON u2.id = tr3.created_by
    LEFT JOIN  ticket_type tt  ON tt.id = tr3.ticket_type_id
    LEFT JOIN  customer c  ON c.id = tr3.customer_id
    ORDER BY  tr3.created_date

The JOINs , after the one in the "derived" table tr2, are touching only 20 rows; this is much of the speedup.

This may be equally good:

SELECT  d.id, d.request_status, d.note, d.created_date,
        c.name AS customer_name, c.mobile_phONe, d.created_by_name,
        tt.name AS ticket_type_name
    FROM  
    (
        SELECT  tr.id AS tr_id, tr.request_status, tr.note, tr.created_date,
                tr.ticket_type_id, tr.customer_id
                u.full_name AS created_by_name
            FROM  ticket_request tr
            JOIN  users u  ON u.id = tr.created_by
            WHERE  u.status = '1'
              AND  tr.is_deleted != 1
            ORDER BY  tr.created_date DESC
            LIMIT  0,20 
    ) AS d
    LEFT JOIN  ticket_type tt  ON tt.id = d.ticket_type_id
    LEFT JOIN  customer c  ON c.id = d.customer_id
    ORDER BY  d.created_date

I'm assuming that you are using MySQL. If not, this answer can be slightly modified to fit another database, but the concept should remain the same. You can add indices to all the ID columns which are involved in the right hand side of your left joins with the ticket_request column:

ALTER TABLE ticket_type ADD INDEX (id);
ALTER TABLE users ADD INDEX (id);
ALTER TABLE customer ADD INDEX (id);      -- important

To explain why an index would help, consider the first LEFT JOIN between your ticket_request table and the ticket_type table. Without an index, for each record in ticket_request the database would have to potentially scan the entire ticket_type table to find records which match the join condition. This is costly from a performance point of view. But with an index, the database can complete this operation much faster, since it "knows" where to look exactly (or almost exactly) for the matching records.

Though you mentioned that only the customer table is very large, you can still add indices to the other tables. In the future, they might get larger too. Most likely the join involving customer is the bottleneck in your query.

The biggest opportunity for optimization here is with LIMIT 0,20

  1. GROUP BY tr.id make no sense and should be removed.

  2. create index ticket_request_ix_is_deleted_created_date on ticket_request (is_deleted,created_date) and change tr.is_deleted != 1 to tr.is_deleted = 0 .

    Or

    create index ticket_request_ix_created_date on ticket_request (created_date)

SELECT 
    tr.id, tr.request_status, tr.note, tr.created_date, 
    c.name AS customer_name, c.mobile_phONe, 
    u.full_name AS created_by_name, tt.name AS ticket_type_name 
FROM  
    ticket_request tr 
LEFT JOIN 
    ticket_type tt ON tt.id = tr.ticket_type_id and tr.is_deleted != 1 
LEFT JOIN 
    users u ON u.id = tr.created_by 
JOIN 
  users u1 ON u1.id  = tr.user_id and u1.status = '1'
LEFT JOIN 
    customer c ON c.id = tr.customer_id 
GROUP BY 
    tr.id  
ORDER BY 
    tr.created_date DESC 
LIMIT 0,20 

try this it will work with improved performance and tweak as per your requirement

Other than indexing , On application level you can use Memcached ( in case you are using php) like stuffs. This will also give you great performance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM