Reputation: 173
I have 2 tables:
account
+----+---------------------+
| Id | Email |
+----+---------------------+
| 1 | "[email protected]" |
| 2 | "[email protected]" |
| 3 | "[email protected]" |
| 4 | "[email protected]" |
+----+---------------------+
character
+----+-----------+
| Id | AccountId |
+----+-----------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| 5 | 4 |
+----+-----------+
character.AccountId
is a FK to account.Id
. Both Id
columns are PK's to their respective tables.
I need to update the character
table such that the new AccountId
matches a row in account
with the lowest account's Id
but with the same Email
as the currently set AccountId
.
For example, in the mock data presented above all accounts have unique emails except account Id 1
and 4
, they both share [email protected]
as email.
This means that after the update, the rows in the character table should stay the same except for the row with Id = 5
, this row has an AccountId = 4
and this account shares an email with an account that has a lower account Id, namely Id 1
. So the result output should be :
+----+-----------+
| Id | AccountId |
+----+-----------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| 5 | 1 |
+----+-----------+
After the operation.
I've got this to work with a TRIGGER BEFORE INSERT
on character
to check if there are lower accountIds but can't get this to work with a simple UPDATE
:
UPDATE `character` SET `AccountId` = (SELECT MIN(Id) FROM `account` WHERE ?);
I've thought of maybe making a temporary table to store the account Id and the email with a GROUP BY email
but this also fails. It being MySQL I can't use MERGE
either.
Upvotes: 0
Views: 82
Reputation: 98423
This is fairly straightforward without window functions, too; you want to find accounts with the same email and lower ids (the a2 join) and make sure you've got the lowest (the a3 join):
update `character` c
join account a on a.id=c.account_id
join account a2 on a2.email=a.email and a2.id < a.id
left join account a3 on a3.email=a2.email and a3.id < a2.id
set c.account_id=a2.id
where a3.id is null;
(As usual, the left join ... where ... is null could be replaced with a where not exists (...), if you prefer the query to show the intent more clearly instead of the probable query plan more clearly.)
Upvotes: 1
Reputation: 1270873
You can use a window function to get the minimum id per email. Then just join that in:
UPDATE character c JOIN
(SELECT a.*,
MIN(id) OVER (PARTITION BY a.email) as min_id
FROM account a
) a
ON c.accountId = a.id
SET c.accountId = a.min_id
WHERE c.accountId <> a.min_id;
Upvotes: 2