Reputation: 2428
In MySQL I have these four tables:
quotes
id | uid | tid | message | time
quotes_rnd
id | message
users
idu | username
authors
id | uid | name
I need to populate the quotes
table so this is my procedure:
BEGIN
INSERT INTO quotes
(message)
SELECT message
FROM quotes_rnd ORDER BY RAND() limit 1;
UPDATE quotes SET tid = (
SELECT id FROM authors WHERE id>10 ORDER BY rand() LIMIT 1)
WHERE tid = 0;
UPDATE quotes SET uid = (
SELECT idu FROM users WHERE idu BETWEEN 13 AND 63 ORDER BY rand() LIMIT 1)
WHERE uid = 0;
END
The problem
UPDATE quotes SET uid = (
SELECT idu FROM users WHERE idu BETWEEN 13 AND 63 ORDER BY rand() LIMIT 1)
WHERE uid = 0;
Before this query line, the tid
value is already set, instead the uid=0
.
How to SET uid
only when uid.tid
of all rows inside the table quotes
is different from the one randomized here for the last row?
Upvotes: 1
Views: 82
Reputation: 95101
Dear reader: The first two solutions fail with
MySQL: #1093 - You can't specify target table 'upd' for update in FROM clause"
I keep them here, though, as to make the problem known. Please scroll down to "EDIT" to see my third solution, which is working and is the one that's been accepted :-)
You say you want to try once with a random user and stay with uid = 0 in case of duplicate uid + tid. For this you'd use an UPDATE with a join:
UPDATE quotes upd
CROSS JOIN (SELECT idu FROM users WHERE idu BETWEEN 13 AND 63 ORDER BY rand() LIMIT 1) u
SET upd.uid = u.iud
WHERE upd.uid = 0
AND NOT EXISTS
(
select *
from quotes q
where q.tid = upd.tid
and q.uid = u.uid
);
If, however, you wanted to find a non-duplicating user as long as possible, you'd use a correlated subquery:
UPDATE quotes upd
SET uid = coalesce
(
(
SELECT idu
FROM users u
WHERE idu BETWEEN 13 AND 63
AND NOT EXISTS
(
select *
from quotes q
where q.tid = upd.tid
and q.uid = u.uid
)
ORDER BY rand() LIMIT 1
), 0
)
WHERE uid = 0;
The difference: You take all non-duplicating users into account. As long as there is at least one, you will update. Whereas with the first update statement, you'd just try just one random user, and either it's a non-duplicationg one then you'd update, else not.
EDIT: I am not fluent in MySQL yet, so I don't know how to overcome the MySQL limitation here quickly. If, however, you like the idea behind the second approach, then you might even want to strive to get tid/uid pairs as long as possible, i.e. don't update with a used-up tid when there are still tid/uid pairs for other tids available. You could do this all on insert:
INSERT INTO quotes (message, tid, uid)
SELECT q.message, a.id, coalesce(u.idu,0)
FROM quotes_rnd q
JOIN authors a ON a.id > 10
LEFT JOIN users u ON u.idu BETWEEN 13 AND 63
AND NOT EXISTS
(
select *
from quotes q
where q.tid = a.id
and q.uid = u.idu
)
ORDER BY (u.idu IS NULL), rand() LIMIT 1;
(This is kind of cross-joining in order to find all possible pairs, so it might be slow on large tables.)
SQL fiddle: http://www.sqlfiddle.com/#!9/7b756b/1.
Upvotes: 1