简体   繁体   中英

How can I optimize a SQL query?

SELECT *
FROM account a1 
LEFT JOIN account a2 ON a1.group_id>0 AND a2.app_id=0 AND a1.group_id=a2.group_id AND a1.id!=a2.id 
JOIN user_account_ref uar ON uar.account_id=a1.id OR (uar.account_id=a2.id AND uar.role>0) 
WHERE uar.user_id IN ($some_user_ids);

When I replace the where clause by WHERE a1.id IN ($some_account_ids) , the query is fast, but it's very slow for querying in user ids. I tried to use subqueries but I wasn't able to make it work.

The table schema is like below:

---account---
id
app_id
group_id

---user_account_ref---
id
user_id
account_id
role

Some more infos:

select * from user_account_ref where user_id IN (xxx)

is fast and has 4 rows in result.

SELECT * 
FROM account a1 
LEFT JOIN account a2 ON a1.group_id>0 AND a2.app_id=0 AND a1.group_id=a2.group_id AND a1.id!=a2.id

is also fast and has 2000+ rows in result.

Please try these three indexes and see if they solve your performance issue. (Then look at the explain plan to see which of these indexes don't get used and drop them.)

create index idx1 on account (group_id, id, app_id);
create index idx2 on user_account_ref (user_id, account_id);
create index idx3 on user_account_ref (account_id, user_id);

idx1 is for speeding up your account self-join. idx2 and idx3 are two offers to the DBMS to quickly join the user_account_ref table. Hopefully, the DBMS will pick one of them and get the query execution faster thus.

If these indexes don't help, you may try to drop only idx3 first, so as to kind of force idx2 , as it seems that the condition on users greatly decreases the rows to look at and the DBMS may not be waware of this.

At last you may be able to update MySQLs table statistics, so the DBMS knows your tables better. Good luck.

I finally used the UNION query suggested by @Akina. And I added a index on group_id which is reminded by @Thorsten Kettner. Thanks to both of you.

Here is my new query:

SELECT a.id, uar.* 
FROM account a
JOIN user_account_ref uar ON uar.account_id=a.id AND uar.status=0
WHERE uar.user_id IN (xxx)

UNION

SELECT a.id, uar.* 
FROM account a,
(
    SELECT DISTINCT ra.id, ra.group_id
    FROM account ra
    JOIN user_account_ref uar ON ra.id=uar.account_id and ra.app_id=0
    WHERE uar.user_id in (xxx) AND ra.group_id>0
) ra
JOIN user_account_ref uar ON uar.role>0
WHERE a.group_id=ra.group_id AND uar.account_id=ra.id AND uar.user_id IN (xxx);

If this is fast:

SELECT a1.*, a2.* 
FROM account a1 LEFT JOIN
     account a2
    ON a1.group_id > 0 AND 
       a2.app_id = 0 AND
       a1.group_id = a2.group_id AND
       a1.id <> a2.id;

Then use two left join s:

    SELECT a1.*, a2.*, COALESCE(uar1.role, uar2.role) as role
    FROM account a1 LEFT JOIN
         account a2
         ON a1.group_id > 0 AND 
            a2.app_id = 0 AND
            a1.group_id = a2.group_id AND
            a1.id <> a2.id LEFT JOIN
         user_account_ref uar1 AND uar2.role > 0
         ON uar1.account_id = a1.id LEFT JOIN
         user_account_ref uar2
         ON uar2.account_id = a2.id AND uar2.role > 0
WHERE uar1.user_id IN ($some_user_ids) OR
      uar2.user_id IN ($some_user_ids) ;

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