I've created a web app that lets you send money to other people (like paypal) for an University project.
For the send money webpage there's a form where an user can choose the money receiver's ID and the amount to send.
The system works with these queries:
SET #moneytosend= ? ; //amount to send
START TRANSACTION;
UPDATE users SET balance= balance- #moneytosend WHERE id = ?; //sender's ID
UPDATE users SET balance= balance+ #moneytosend WHERE id = ?; //receiver's ID
COMMIT WORK;
The problem is when the user inserts a non existing ID and the query is still executed, effectively removing money from the sender.
I'm sorry if my english is not perfect, and sorry again if there are any formatting errors.
One method is to check that the users exist in the update queries:
UPDATE users JOIN
(SELECT ? as sender_id, ? as receiver_id
) uu
ON u.id IN (uu.sender_id, uu.receiver_id)
SET balance = balance + (CASE WHEN u.id = uu.receiver_id THEN #moneytosend ELSE - #moneytosend)
WHERE EXISTS (SELECT 1 FROM users u2 WHERE u2.id = uu.sender_id) AND
EXISTS (SELECT 1 FROM users u2 WHERE u2.id = uu.receiver_id);
This logic combines the queries into a single query and still allows you to input only two parameters.
The problem is caused in the 1st update statement and it can be solved by adding EXISTS as a condition:
update users
set balance = balance - #moneytosend
where
balance >= #moneytosend
and
id = ? <-- sender id
and
exists (
select 1 from (select * from users where id = ? <-- receiver id
) t);
See the demo
As suggested by Raymond Nijland, do an extra check so that balance does not get a negative value.
I have below query in mysql where I want to check if branch id and year of finance type from branch_master are equal with branch id and year of manager then update status in manager table against branch id in manager
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
SELECT m2.branch_id FROM manager as m2
WHERE (m2.branch_id,m2.year) IN (
(
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance'
)
)
)
but getting error
Table 'm1' is specified twice, both as a target for 'UPDATE' and as a
separate source for data
This is a typical MySQL thing and can usually be circumvented by selecting from the table derived, i.e. instead of
FROM manager AS m2
use
FROM (select * from manager) AS m2
The complete statement:
UPDATE manager
SET status = 'Y'
WHERE branch_id IN
(
select branch_id
FROM (select * from manager) AS m2
WHERE (branch_id, year) IN
(
SELECT branch_id, year
FROM branch_master
WHERE type = 'finance'
)
);
The correct answer is in this SO post.
The problem with here accepted answer is - as was already mentioned multiple times - creating a full copy of the whole table. This is way far from optimal and the most space complex one. The idea is to materialize the subset of data used for update only, so in your case it would be like this:
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
SELECT * FROM(
SELECT m2.branch_id FROM manager as m2
WHERE (m2.branch_id,m2.year) IN (
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance')
) t
)
Basically you just encapsulate your previous source for data query inside of
SELECT * FROM (...) t
Try to use the EXISTS operator:
UPDATE manager as m1
SET m1.status = 'Y'
WHERE EXISTS (SELECT 1
FROM (SELECT m2.branch_id
FROM branch_master AS bm
JOIN manager AS m2
WHERE bm.type = 'finance' AND
bm.branch_id = m2.branch_id AND
bm.year = m2.year) AS t
WHERE t.branch_id = m1.branch_id);
Note: The query uses an additional nesting level, as proposed by #Thorsten, as a means to circumvent the Table is specified twice error.
Demo here
Try :::
UPDATE manager as m1
SET m1.status = 'Y'
WHERE m1.branch_id IN (
(SELECT DISTINCT branch_id
FROM branch_master
WHERE type = 'finance'))
AND m1.year IN ((SELECT DISTINCT year
FROM branch_master
WHERE type = 'finance'))
The problem I had with the accepted answer is that create a copy of the whole table, and for me wasn't an option, I tried to execute it but after several hours I had to cancel it.
A very fast way if you have a huge amount of data is create a temporary table:
Create TMP table
CREATE TEMPORARY TABLE tmp_manager
(branch_id bigint auto_increment primary key,
year datetime null);
Populate TMP table
insert into tmp_manager (branch_id, year)
select branch_id, year
from manager;
Update with join
UPDATE manager as m, tmp_manager as tmp_m
inner JOIN manager as man on tmp_m.branch_id = man.branch_id
SET status = 'Y'
WHERE m.branch_id = tmp_m.branch_id and m.year = tmp_m.year and m.type = 'finance';
This is by far the fastest way:
UPDATE manager m
INNER JOIN branch_master b on m.branch_id=b.branch_id AND m.year=b.year
SET m.status='Y'
WHERE b.type='finance'
Note that if it is a 1:n relationship the SET command will be run more than once. In this case that is no problem. But if you have something like "SET price=price+5" you cannot use this construction.
Maybe not a solution, but some thoughts about why it doesn't work in the first place:
Reading data from a table and also writing data into that same table is somewhat an ill-defined task. In what order should the data be read and written? Should newly written data be considered when reading it back from the same table? MySQL refusing to execute this isn't just because of a limitation, it's because it's not a well-defined task.
The solutions involving SELECT ... FROM (SELECT * FROM table) AS tmp just dump the entire content of a table into a temporary table, which can then be used in any further outer queries, like for example an update query. This forces the order of operations to be: Select everything first into a temporary table and then use that data (instead of the data from the original table) to do the updates.
However if the table involved is large, then this temporary copying is going to be incredibly slow. No indexes will ever speed up SELECT * FROM table.
I might have a slow day today... but isn't the original query identical to this one, which souldn't have any problems?
UPDATE manager as m1
SET m1.status = 'Y'
WHERE (m1.branch_id, m1.year) IN (
SELECT DISTINCT branch_id,year
FROM `branch_master`
WHERE type = 'finance'
)
I'm wondering if it's possible to delete columns using some aggregate functions (found here: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html)
For example say I wanted to delete the last user from my database, it'd be easy if I could just run:
delete from user where id = MAX(id);
But running this will give me the following error:
ERROR 1111 (HY000): Invalid use of group function
Is this possible with mysql or not?
You have to use a query like this:
delete from user where id = (select id
from (select MAX(id) as id
from user) as t)
Try with another select as follows:
delete from user where id = (SELECT MAX(id) FROM user);
you could try something like:
delete from user where id = (select max(id) from user);
As another option, is to use temporary variable:
set #id = (select max(id) from user);
delete from user where id = #id;
I want to run an Update where the WHERE statement consists of 2 SELECTS, is this at all close to how you do that?
UPDATE Requests SET Response=1 WHERE
sender=SELECT userID FROM Users WHERE Username=?) and
Reciever = SELECT userID FROM Users WHERE Username=?
Thank!
You're missing parentheses, that's all.
UPDATE Requests SET Response=1 WHERE
sender = (SELECT userID FROM Users WHERE Username=?) and
receiver = (SELECT userID FROM Users WHERE Username=?);
This is my mysql query, but it is not working
update jos_users set name =
(SELECT name
FROM jos_users
WHERE id = 478) where id =477
can any one please tell how to execute this query? or other possibility?
the error-message you should get is:
#1093 - You can't specify target table 'jos_users' for update in FROM clause
wich means you cant use the same table you're updating in a subselect. anywa, there's a litte workaround to avoid this: just use a nested subselect instead:
update
jos_users
set
name = (select name from
(SELECT name FROM jos_users WHERE id = 478)
AS subselect_value)
where
id = 477