简体   繁体   中英

MySQL alternative to using a subquery

So let's say I have the following tables Person and Wage. It's a 1-N relation, where a person can have more then one wage.

**Person**
id
name

**Wage**
id
person_id
amount
effective_date

Now, I want to query a list of all persons and their latest wages. I can get the results by doing the following query:

SELECT 
   p.*, 
   (  SELECT w.amount 
      FROM wages a w 
      WHERE w.person_id = p.id 
      ORDER BY w.effective_date 
      LIMIT 1 
   ) as wage_amount,
   (  SELECT w.effective_date 
      FROM wages a w 
      WHERE w.person_id = p.id 
      ORDER BY w.effective_date 
      LIMIT 1 
   ) as effective_date
FROM person as p

The problem is, my query will have multiple sub-queries from different tables. I want to make it as efficient as possible. Is there an alternative to using sub-queries that would be faster and give me the same results?

Subqueries can be a good solution like Sam S mentioned in his answer but it really depends on the subquery, the dbms you are using, and your indexes. See this question and answers for a good discussion on the performance of subqueries vs. joins: Join vs. sub-query

If performance is an issue for you, you must consider using the EXPLAIN command of your dbms. It will show you how the query is being built and where the bottlenecks are. Based on its results, you might consider rewriting your query some other way.

For instance, it was usually the case that a join would yield better performance, so you could rewrite your query according to this answer: https://stackoverflow.com/a/2111420/362298 and compare their performance.

Note that creating the right indexes will also make a big difference.

Hope it helps.

Proper indexing would probably make your version work efficiently (that is, an index on wages(person_id, effective_date) ).

The following produces the same results with a single subquery:

SELECT p.*, w.amount, w.effective_date
from person p left outer join
     (select person_id, max(effective_date) as maxdate
      from wages
      group by personid
     ) maxw
     on maxw.person_id = p.id left outer join
     wages w
     on w.person_id = p.id and w.effective_date = maxw.maxdate;

And this version might make better us of indexes than the above version:

SELECT p.*, w.amount, w.effective_date
from person p left outer join
     wages w
     on w.person_id = p.id
where not exists (select * from wages w2 where w2.effective_date > w.effective_date);

Note that these version will return multiple rows for a single person, when there are two "wages" with the same maximum effective date.

Subqueries are very efficient as long as you make sure you use indexes. Try running EXPLAIN on your query and see if it uses correct indexes

SELECT p.name, w.amount, MAX(w.effective_date) FROM Person p LEFT JOIN Wage w ON w.person_id = p.id GROUP BY p.name

I didn't test this query.

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