Ryan K
Ryan K

Reputation: 346

MySQL error 1242 - Subquery returns more than 1 row

i have two tables in a DB with the following structure:

table 1: 3 rows - category_id, product_id and position

table 2: 3 rows - category_id, product_id and position

i am trying to set table 1 position to table 2 position where category and product id is the same from the tables.

below is the sql i have tried to make this happen but returns MySQL error 1242 - subquery returns more then 1 row

UPDATE table1 
SET position = (
    SELECT position 
    FROM table2 
    WHERE table1.product_id = table2.product_id AND table1.category_id = table2.category_id
)

Upvotes: 1

Views: 2420

Answers (2)

axiac
axiac

Reputation: 72276

The solution is very simple and it can be done in two simple steps. The first step is just a preview of what will be changed, to avoid destroying data. It can be skipped if you are confident of your WHERE clause.

Step 1: preview the changes

Join the tables using the fields you want to match, select everything for visual validation of the match.

SELECT t1.*, t2.*
FROM table1 t1
    INNER JOIN table2 t2
        ON t1.category_id = t2.category_id
        AND t1.product_id = t2.product_id

You can also add a WHERE clause if only some of the rows must be modified.

Step2: do the actual update

Replace the SELECT clause and the FROM keyword with UPDATE, add the SET clause where it belongs. Keep the WHERE clause:

UPDATE table1 t1
    INNER JOIN table2 t2
        ON t1.category_id = t2.category_id
        AND t1.product_id = t2.product_id
SET t1.position = t2.position

That's all.

Technical considerations

Indexes on the columns used on the JOIN clause on both tables are a must when the tables have more than several hundred rows. If the query doesn't have WHERE conditions then MySQL will use indexes only for the biggest table. Indexes on the fields used on the WHERE condition will speed up the query. Prepend EXPLAIN to the SELECT query to check the execution plan and decide what indexes do you need.

You can add SORT BY and LIMIT to further reduce the set of changed rows using criteria that cannot be achieved using WHERE (for example, only the most recent/oldest 100 rows etc). Put them on the SELECT query first to validate the outcome then morph the SELECT into an UPDATE as described. Of course, indexes on the columns used on the SORT BY clause are a must.

Upvotes: 1

Gordon Linoff
Gordon Linoff

Reputation: 1270703

You can run this query to see what is happening:

SELECT product_id, category_id, count(*), min(position), max(position)
FROM table2
GROUP BY product_id, category_id
HAVING COUNT(*) > 1;

This will give you the list of product_id, category_id pairs that appear multiple times in table2. Then you can decide what to do. Do you want an arbitrary value of position? Is the value of position always the same? Do you need to fix the table?

It is easy enough to fix the particular problem by using limit 1 or an aggregation function. However, you may really need to fix the data in the table. A fix looks like:

UPDATE table1 t1
    SET t1.position = (SELECT t2.position 
                       FROM table2  t2
                       WHERE t2.product_id = t1.product_id AND t2.category_id = t1.category_id
                       LIMIT 1
                      );

Upvotes: 0

Related Questions