mysql update column then select updated value - mysql

I have a table like this
tbl_user
id
user_id
amount
first i want to update a row based on id
$amount = 123; // dyanamic value
$sql = "UPDATE tbl_user SET amount=amount-'$amount' WHERE id='$id' LIMIT 1 ";
now i want to get updated value of amount column i have applied this sql
$sql = "SELECT amount FROM tbl_user WHERE id='$id' LIMIT 1 ";
my question is can i combine both of above sql or any single query to achieve above task?

The best you could imitate is to use two lines of queries, probably using a variable like:
UPDATE tbl_user SET
amount = #amount := amount-'$amount'
WHERE id='$id' LIMIT 1;
SELECT #amount;
The best you could do then is to create a Stored Procedure like:
DELIMITER //
CREATE PROCEDURE `return_amount` ()
BEGIN
UPDATE tbl_user SET
amount = #amount := amount-'$amount'
WHERE id='$id' LIMIT 1;
SELECT #amount;
END //
And then call Stored Procedure in your PHP.
Note: PostgreSQL has this kind of option using RETURNING statement that would look like this:
UPDATE tbl_user SET amount=amount-'$amount'
WHERE id='$id' LIMIT 1
RETURNING amount
See here

A function can do this easily. It sounds like you want to limit how many times your code connects to the database. With a stored function or procedure, you are only making one connection. Yes, the stored function has two queries inside it (update then select), but these are executed on the server side without stopping to do round trips to the client.
http://sqlfiddle.com/#!2/0e6a09/1/0
Here's my skeleton of your table:
CREATE TABLE tbl_user (
id VARCHAR(100) PRIMARY KEY,
user_id VARCHAR(100),
amount DECIMAL(17,4) );
INSERT INTO tbl_user VALUES ('1', 'John', '100.00');
And the proposed function:
CREATE FUNCTION incrementAmount
(p_id VARCHAR(100), p_amount DECIMAL(17,4))
RETURNS DECIMAL(17,4)
BEGIN
UPDATE tbl_user
SET amount = amount + p_amount
WHERE id = p_id;
RETURN (SELECT amount FROM tbl_user WHERE id = p_id);
END
//
Then you just run one query, a SELECT on the function you just created:
SELECT incrementAmount('1', 5.00)
The query result is:
105

It is not possible with a single query, but you can combine multiple commands into a script and execute them with a single request to the database server.
Run this script:
"UPDATE tbl_user SET amount=amount-'$amount' WHERE id='".$id."';SELECT amount FROM tbl_user WHERE id='".$id."'; "
Also, you might want to check whether $id is a number, as I do not see a protection against SQL injection inside your code. SQL injection is a serious threat, you would do better to prepare and protect yourself against it.

We can also use:
UPDATE tbl_user SET id = LAST_INSERT_ID(id), amount = 2.4,user_id=4 WHERE id = 123;
// SELECT
$id =SELECT LAST_INSERT_ID();
SELECT amount,user_id FROM tbl_user WHERE id = $id LIMIT 1

Here would be the procedure
CREATE PROCEDURE UpdateAndSelect
(
#amount MONEY,
#id INT
)
AS
BEGIN
UPDATE tbl_user
SET amount = #amount
WHERE id = #id
LIMIT 1
SELECT amount
FROM tbl_user
WHERE id = #id
LIMIT 1
END
GO
You would call this stored procedure by setting your variables (#amoutn and #id) and then calling:
exec UpdateAndSelect
Hope this helps solve your problem

Related

Check condition and send out parameter value from procedure in MySQL

I'm creating a procedure where it needs to check the value of a column IsLoggedIn and send the outparameter with -1 if "IsLoggedIn" is 1 else it needs to send the user information.
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `sp_bh_loginverify1`(IN `user_email` VARCHAR(100), OUT `p_islogged` INT)
NO SQL
BEGIN
IF (EXISTS(select * FROM user_details WHERE user_email = user_email))
THEN
SET p_islogged = 1; SELECT 0;
END IF;
(select UD.user_id, UD.user_name, UD.password, UD.password_salt, UD.user_email, R.role_id as role, R.role_name as role_name from user_details UD
JOIN roles R on R.role_id = UD.role
where (UD.user_email = user_email OR UD.user_name = user_email));
END$$
DELIMITER ;
First of all, always name your procedure input parameters something different from the columns of your table. For example you do this:
IF (EXISTS(select * FROM user_details WHERE user_email = user_email))
How is MySQL supposed to know that user_email is the column in the table, but user_email is the procedure parameter of that name? It doesn't -- it assumes both are references to the column. It compares the column user_email to itself, which will return every row where the column is not NULL, because it's always true that user_email = user_email. In other words, that the column is equal to itself (unless it's NULL).
You shoudl name the procedure parameter something distinct from the column name:
CREATE DEFINER=`root`#`localhost` PROCEDURE `sp_bh_loginverify1`(IN `p_user_email` VARCHAR(100), OUT `p_islogged` INT)
You used NO SQL which means: "indicates that the routine contains no SQL statements." But that's not true, because you do have SQL statement in this procedure.
You should use:
READS SQL DATA
You said you want to return -1 in the OUT parameter, but you set 1. So you should set -1.
Next you mentioned "else it sends user information" which suggests you want that result set to be returned only conditionally. But your code returns that user information outside the IF/END IF block, so it happens regardless of the condition. You should use an ELSE block to do this.
Notice the use of p_user_email in the queries below, to make it distinct from user_email.
BEGIN
IF (EXISTS(select * FROM user_details WHERE user_email = p_user_email))
THEN
SET p_islogged = -1;
ELSE
SELECT UD.user_id, UD.user_name, UD.password, UD.password_salt, UD.user_email,
R.role_id as role, R.role_name as role_name
FROM user_details UD
JOIN roles R ON R.role_id = UD.role
WHERE UD.user_email = p_user_email OR UD.user_name = p_user_email;
END IF;
END
I don't think you need SELECT 0; at all. MySQL stored procedures can return no result set, or one result set, or multiple result sets.

Update database sortorder column based on array value list

I need to update the sort order column based on the sequence of primary key values I get as array list from ajax call. For example I have 2 coulmns (ID, Sortorder) with values (23,1)(32,2)(21,3)(43,4), now the user from the frontend moves the 3rd row(21,3) above second row(32,2) and I get the ID array sequence as 23, 21, 32, 43 which I have to maintain. From this list, I am trying to update the sororder as per the sequence, so the database table values should look as (23,1)(32,3)(21,2)(43,4). Could you help me to get this DB update statement.
Attached the print screen for better understanding:
Java logic I have, trying to find an update sql statement to loop from an array list. I have ~1000 rows in my table and with my logic, this would trigger 1000 update queries, I don't think this is efficient. Trying to find an alternate efficient way.
Connection conn = null;
PreparedStatement pstmt = null;
conn = getConnection();
String query = "update Sortordertable set sortorder = ? where Id = ? ";
pstmt = conn.prepareStatement(query); // create a statement
String str[]=String.valueOf(s.getRecordId()).split(";");//id1;id;id3;.... list I get from ajax call
for(int i=0;i<str.length();i++)
{
pstmt.setId(1,i++); //set sortorder value as 1, 2, 3..
pstmt.setInt(2, str[i]); // In this line I want to use my array-list to update my table.
pstmt.executeUpdate(); // execute update statement
}
For the specific use case you have showed, you could use the following query, which would update only the rows with id=32 or id=23.
UPDATE t1 SET
sortorder = CASE WHEN id = 32 THEN 3 ELSE 2 END
WHERE id IN (32, 21);
This could be adapted for multiple updates if you don't update the database after each operation. but will grow with the number of operations made by the user before the update is triggered.
Edit to address comment:
If, as in the example given in your comment, you want to move the row with order 4 to the first row, you can use the following:
UPDATE t1 SET
sortorder = CASE WHEN sortorder = 4 THEN 1 ELSE sortorder + 1 END
WHERE sortorder <= 4;
I've added a where clause in that last query to illustrate that you can easily adapt this to different use case.
Here's what you can do:
DELIMITER $$
CREATE PROCEDURE `sp_update_positions`(
IN `p_positions` VARCHAR(255)
)
BEGIN
SET #positions = REPLACE(p_positions, ',', '),(');
SET #positions = CONCAT('(', #positions, ')');
DROP TEMPORARY TABLE IF EXISTS tmpPositions;
CREATE TEMPORARY TABLE tmpPositions(
`position` int(10) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
`order_id` int(10) unsigned NOT NULL
);
SET #insertString = CONCAT('INSERT INTO tmpPositions (`order_id`) VALUES ', #positions);
PREPARE insertStatement FROM #insertString;
EXECUTE insertStatement;
DEALLOCATE PREPARE insertStatement;
UPDATE orders
INNER JOIN tmpPositions ON order_id = orders.id
SET orders.Sortorder = tmpPositions.position;
SELECT ROW_COUNT() AS rowCount;
END$$
DELIMITER ;
Then you call it like this:
CALL sp_update_positions('24,23,21,22');

MYSQL multiple Triggers

I have a MYSQL table and two of the fields are called Rate_per_unit and Cost. First I want the field Rate_per_unit to populate itself from another table called SHD_TEACHER then I want the field COST to populate itself also from RATE in SHD_TEACHER and multiplies by UNITS.
I have the following code which is giving me an error:
CREATE TRIGGER RATE_PER_UNIT_1
BEFORE INSERT ON SHD_SCHEDULE
FOR EACH ROW
SET NEW.RATE_PER_UNIT =
(
SELECT RATE
FROM SHD_TEACHER
WHERE TEACHERID = NEW.TEACHER_ID
LIMIT 1
)
SET NEW.COST = (
SELECT RATE
FROM SHD_TEACHER
WHERE TEACHERID = NEW.TEACHER_ID
) * UNITS
Any help please?
thanks
using your syntax, I would expect a delimiter statement and a begin/end block. So, try this:
DELIMITER $$
CREATE TRIGGER RATE_PER_UNIT_1
BEFORE INSERT ON SHD_SCHEDULE
FOR EACH ROW
BEGIN
SET NEW.RATE_PER_UNIT =
(
SELECT RATE
FROM SHD_TEACHER t
WHERE t.TEACHERID = NEW.TEACHER_ID
LIMIT 1
)
SET NEW.COST = (
SELECT t.RATE
FROM SHD_TEACHER t
WHERE t.TEACHERID = NEW.TEACHER_ID
) * NEW.UNITS
END $$
DELIMITER ;
You have a limit 1 in the first subquery, suggesting that there might be multiple matches. If so, you will get a run-time error in the second. Also, UNITS is just hanging out there, all alone. I assumed it is in the NEW record.
Here is another way to write this:
DELIMITER $$
CREATE TRIGGER RATE_PER_UNIT_1
BEFORE INSERT ON SHD_SCHEDULE
FOR EACH ROW
BEGIN
SELECT NEW.RATE_PER_UNIT := t.RATE, NEW.COST := t.RATE * NEW.UNITS
FROM (SELECT t.*
FROM SHD_TEACHER t
WHERE t.TEACHERID = NEW.TEACHER_ID
LIMIT 1
) t
END $$
DELIMITER ;

Return row ID from StoredProcedure MySQL

I am attempting to return the value of the row for this statement:
INSERT INTO SystemName (SystemName)
SELECT * FROM (SELECT v_SystemName) AS tmp
WHERE NOT EXISTS (
SELECT * FROM SystemName WHERE SystemName = v_SystemName
) LIMIT 1;
SET id = LAST_INSERT_ID();
If it actually does the insert I need it to return that row ID, otherwise it needs to return the new row ID. I tried to use LAST_INSERT_ID() but that didn't return what I needed because if it didn't do the insert it would give the wrong result. Does anyone have an idea that could make this work?
http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_last-insert-id
Assuming your procedure only expects back the row ID and no further processing will be done you can try: SELECT LAST_INSERT_ID();
So, what I ended up doing was this, and I know it isn't the best solution because it isn't a transaction and could cause some issues but this is the only way I could get it to work.
INSERT INTO SystemName (SystemName)
SELECT * FROM (SELECT v_SystemName) AS tmp
WHERE NOT EXISTS (
SELECT * FROM SystemName WHERE SystemName = v_SystemName
) LIMIT 1;
SELECT id FROM SystemName WHERE SystemName = v_SystemName;
This way I don't need any out variables or anything, it just returns my id.

MySQL return to first record using LIMIT

Suppose:
$sql = " SELECT * FROM `artwork` WHERE approved = '1' ";
if($filter) $sql .= " AND $filter = '$filter_value' ";
$sql .= " ORDER BY subdate DESC, id DESC
LIMIT 30,30";
If I were to introduce a starting point (eg. WHERE id > 50) and that stipulation affected my LIMIT such that it only returned 10 results. I want 30 results, remember. Is there a way to start from record 1 and continue the selection?
edit: I realize I'm asking for id > 50 in this example and most certainly the first record if we were to rewind would have a lower ID. In my scenario that's okay.
Thanks, Jason.
If there are 100 records and you're
only selecting 10 rows (#90 - #100),
you want to get 20 more rows (#1 - #20)
If those are your constraints, I don't think you will be able to get the desired result set from a single query.
Here's a stored procedure which creates a temp table to get the desired result:
DELIMITER //
DROP PROCEDURE IF EXISTS TMP_TABLE_ARTWORK//
CREATE PROCEDURE TMP_TABLE_ARTWORK (_offset INT, _count INT)
BEGIN
DECLARE res_total INT DEFAULT 0;
SELECT COUNT(*) INTO res_total FROM artwork;
CREATE TEMPORARY TABLE IF NOT EXISTS artwork_tmp ( t_pseudo INT, t_old INT, t_subdate DATETIME );
INSERT INTO artwork_tmp ( t_pseudo, t_old, t_subdate ) SELECT artwork.id, artwork.id, artwork.subdate FROM artwork ORDER BY artwork.subdate DESC;
INSERT INTO artwork_tmp ( t_pseudo, t_old, t_subdate ) SELECT ( artwork.id + res_total ), artwork.id, artwork.subdate FROM artwork ORDER BY artwork.subdate DESC;
PREPARE STMT FROM "SELECT * FROM artwork_tmp ORDER BY t_pseudo LIMIT ?,?";
SET #offset = _offset;
SET #count = _count;
EXECUTE STMT USING #offset, #count;
DROP TABLE artwork_tmp;
END //
DELIMITER ;
You'll probably need to modify it to get it to do what you want (and apparently the prepared statement workaround is no longer required if you're running a newer version of MySQL).
If I understand your question.
You should use LIMIT 0, 30 to get first 30 records which match your query.