-1

So my problem is the following. Consider this query:

SELECT
    P.id,
    (SELECT barcode FROM product WHERE publisher_id = P.id LIMIT 1) AS barcode_sample
FROM
    publisher P
WHERE
    P.name LIKE '%tes%'

This is really about just returning the publisher ids and one barcode (any) as an example from the products. Now this query runs fast and gives what I need but I like to keep things at their place so I tried the following query:

SELECT
    P.id,
    B.barcode AS barcode_sample
FROM
    publisher P
    LEFT JOIN
    (
        SELECT
            publisher_id,
            MAX(barcode) AS barcode
        FROM
            product
        GROUP BY
            publisher_id
    ) B
        ON P.id = B.publisher_id
WHERE
    P.name LIKE '%tes%'

Which does almost the same but with a subquery join and group by. Now I'd expect MySQL to first eliminate the rows from the subquery (like a where) to only group by the publisher_id-s I need for the join. But apparently it's not that intelligent and just reads all the rows and does a resource consuming group by.

Here are the explains in order:

+----+--------------------+---------+-------+------------------+------------------+---------+--------------+-------+--------------------------+
| id | select_type        | table   | type  | possible_keys    | key              | key_len | ref          | rows  | Extra                    |
+----+--------------------+---------+-------+------------------+------------------+---------+--------------+-------+--------------------------+
|  1 | PRIMARY            | P       | index | NULL             | name             | 452     | NULL         | 94004 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | product | ref   | idx_publisher_id | idx_publisher_id | 5       | finance.P.id |   210 | Using where              |
+----+--------------------+---------+-------+------------------+------------------+---------+--------------+-------+--------------------------+

+----+-------------+------------+-------+---------------+------------------+---------+------+----------+--------------------------+
| id | select_type | table      | type  | possible_keys | key              | key_len | ref  | rows     | Extra                    |
+----+-------------+------------+-------+---------------+------------------+---------+------+----------+--------------------------+
|  1 | PRIMARY     | P          | index | NULL          | name             | 452     | NULL |    94004 | Using where; Using index |
|  1 | PRIMARY     | <derived2> | ALL   | NULL          | NULL             | NULL    | NULL |    64829 |                          |
|  2 | DERIVED     | product    | index | NULL          | idx_publisher_id | 5       | NULL | 21713216 |                          |
+----+-------------+------------+-------+---------------+------------------+---------+------+----------+--------------------------+

My question is can I somehow achieve what I want in a semantically better (better looking) way or do I have to learn to live with this ugly but fast query?

Updated queries and examine results with a WHERE clause

4
  • what do you mean by 'eliminate the rows from the subquery (like a where)' ? I don't see anything in your example queries showing an attempt to eliminate rows; if you're having performance issues with such a query, I'd suggest updating the question to include such query
    – markp-fuso
    Commented Oct 8, 2018 at 14:55
  • Yeah sorry. The problem is that it's the same when I add a Where Clause to the outer query to filter for the name of the supplier for example. And then there are 10 supplier to show but the subquery still goes through all the rows and not just on the 10 suppliers found
    – emul
    Commented Oct 8, 2018 at 15:07
  • Have you tried pushing the 'where' clause into the sub-query? Some RDBMS engines (eg, Sybase ASE) will try to flatten the query (eg, push a 'where' clause down as early in the plan as possible), so I'm guessing this isn't something MySQL does; you should still update your question with the query you mention in your comment, along with the query plan associated with said query, otherwise your post comes across as incomplete (and not everyone is going to spend their time reading through comments trying to piece together the rest of the puzzle)
    – markp-fuso
    Commented Oct 8, 2018 at 15:16
  • Fair enough. I updated the post with the Where clause. Adding the where into the subquery would require a join there as well what is of course can be done and still better than nothing but I'd be curious if it could be solved without that Thanks
    – emul
    Commented Oct 8, 2018 at 17:46

2 Answers 2

2

What you seem to be looking for is a LATERAL join, which as far as I can tell is not supported by MySQL. Something like:

SELECT P.id, B.barcode AS barcode_sample
FROM publisher P
LEFT JOIN LATERAL (
    SELECT publisher_id, MAX(barcode) AS barcode
    FROM product x
    WHERE x.publisher_id = P.id
    GROUP BY publisher_id
) B
    ON P.id = B.publisher_id
WHERE P.name LIKE '%tes%'

can be used if the DBMS supports LATERAL. Some optimizers will push the predicates inside the sub-query even without LATERAL, but I don't think MySQL's optimizer does.

Anyhow, I'm not sure why you need to join against a sub-select, I believe the following should work:

SELECT P.id, MAX(B.barcode) AS barcode_sample
FROM publisher P
LEFT JOIN product B
    ON P.id = B.publisher_id
WHERE P.name LIKE '%tes%'
GROUP BY P.id 
0

Both need INDEX(publisher_id, barcode). The subquery will be only slightly faster with this index, but the JOIN version may be significantly faster with it.

Unfortunately, P.name LIKE '%tes%' requires scanning the entire table P.

As for "ugly", I see both as ugly. LIMIT 1 picks whatever happens to be first. BTW, adding my index may change the result. MAX implies that you want the 'last' in some sense. Neither quite conveys "just give me any barcode". To temper the ugliness, you say "barcode_sample".

Anyway, Lennart's second suggestion is also 'good'. I vote for it because it avoids subqueries.

But be cautious of GROUP BY if you actually have more columns in the SELECT.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.