Mysql increment in loop by variable - mysql

I am trying to use a simple MySQL query to update my table with positions.
Let's assume I have a book table with an ID, a writer_id and a position field.
I want to be able to have positions from 1 ... x , but per writer_id.
If I delete a record there will be a gap in my positions so this is why I want a simple query to reset all the positions without gaps.
Currently I have the following code (which works), but I think this should be possible a lot easier (and probably faster).
set #position := 0;
set #lastDependency := 0;
set #previousDependency := -1;
UPDATE `book` SET
`writer_id`=(#lastDependency:=`writer_id`), -- Set writer_id of current row
position=(
IF (
NOT #lastDependency=#previousDependency,
#position:=1, -- New writer_id => set position to 1
#position:=#position+1 -- Same writer id, increment position
)
),
`writer_id`=(#previousDependency:=`writer_id`) -- Set writer_id of last used row
ORDER BY `writer_id`, position ASC -- Order by current positions
I can also use PHP to loop through all my records and save them one by one, but I guess that won't be any better

why don't you use a trigger with the following function:
"when a row is deletet, reduce evere writer_id that is greater than the deleted ones by one"
or to say it in pseudo-code:
create trigger for delete...
update book
set writer_id = writer_id - 1
where writer_id > deleted.writer_id

Let me quote the MySQL documentation to you:
As a general rule, you should never assign a value to a user variable and read the value within the same statement. You might get the results you expect, but this is not guaranteed. The order of evaluation for expressions involving user variables is undefined and may change based on the elements contained within a given statement; in addition, this order is not guaranteed to be the same between releases of the MySQL Server.
So the way you attempt to do things may work, but comes with absolutely no guarantees. Therefore I'd suggest you do this in PHP instead of MySQL. Or you do it in MySQL using a stored procedure.

Related

Update A star column till root node on adding new entry

I don't know how to solve this case need some guidance I have a scenario where I have to update a star column field of root node on adding every child entry so far I have done this, its updating the star column of directly linked nodes not the full path like if A is root and B C & D are children then A star column gets update now if C B or D adds something below them A stars column doesn't gets updated, I have less knowledge about triggers and sql and I am stuck I have searched a lot but didn't find a solution here is the method of sql which is doing all this so far.
DELIMITER ##
DROP PROCEDURE p_prefix_nodes_add_new_paths_after_insert ##
CREATE PROCEDURE cvs.p_prefix_nodes_add_new_paths_after_insert
(
param_node_new_id INT UNSIGNED,
param_node_parent_id INT UNSIGNED
)
BEGIN
INSERT INTO `prefix_nodes_paths` (
`ancestor_id`,
`descendant_id`,
`path_length`
)
SELECT
`ancestor_id`,
`param_node_new_id`,
`path_length` + 1
FROM
`prefix_nodes_paths`
WHERE `descendant_id` = `param_node_parent_id`
UNION
ALL
SELECT
`param_node_new_id`,
`param_node_new_id`,
0 ;
Update prefix_nodes_paths
Set stars=stars+1
where ancestor_id=param_node_parent_id;
END ##
DELIMITER ;
Id's suggest you to use the nested set model technique to store your tree-like data in the SQL database. Your queries would become a lot more handy, faster and simpler when you try to get descendants or ancestors of the specific node.

SELECT & UPDATE at the same time - race condition

I'm experiencing a race condition because I'm dealing with a lot of concurrency.
I'm trying to combine these two mysql statements to execute at the same time.
I need to select a row and update the same one...
SELECT id_file FROM filenames WHERE pending=1 LIMIT 1;
UPDATE filenames SET pending=2 WHERE id_file=**id of select query**;
Another solution to the race-condition I'm experiencing would be to perform an UPDATE query where pending=1 and somehow get the ID of the updated row, but I'm not sure if that's even possible?
Thanks
To deal with concurrency is one of the basic functions of transactions.
Wrap your queries into one transaction and tell the DBMS, that you need the row not to change in between with FOR UPDATE:
BEGIN;
SELECT id_file FROM filenames WHERE pending=1 LIMIT 1 FOR UPDATE;
# do whatever you like
UPDATE filenames SET pending=2 WHERE id_file=**id of select query**;
COMMIT;
You can execute these statements with 4 mysqli_query calls, and do whatever you want in between, without need to worry about the consistency of your database. The selected row is save until you release it.
You can avoid the "race" condition by performing just an UPDATE statement on the table, allow that to identify the row to modified, and then subsequently retrieve values of columns from the row.
There's a "trick" returning values of columns, in your case, the value of the id_file column from the row that was just updated. You can use either the LAST_INSERT_ID() function (only if the column is integer type), or a MySQL user-defined variable.
If the value of the column you want to retrieve is integer, you can use LAST_INSERT_ID() function (which supports a BIGINT-64 value).
For example:
UPDATE filenames
SET pending = 2
, id_file = LAST_INSERT_ID(id_file)
WHERE pending = 1
LIMIT 1;
Following the successful execution of the UPDATE statement, you'll want to verify that at least one row was affected. (If any rows satisfied the WHERE, and the statement succeeded, we know that one row will be affected. Then you can retrieve that value, in the same session:
SELECT LAST_INSERT_ID();
to retrieve the value of id_file column of the last row processed by the UPDATE statement. Note that if the UPDATE processes multiple rows, only the value of last row that was processed by the UPDATE will be available. (But that won't be an issue for you, since there's a LIMIT 1 clause.)
Again, you'll want to ensure that a row was actually updated, before you rely on the value returned by the LAST_INSERT_ID() function.
For non-integer columns, you can use a MySQL user-defined variable in a similar way, assigning the value of the column to a user-defined variable, and then immediately retrieve the value stored in the user-defined variable.
-- initialize user-defined variable, to "clear" any previous value
SELECT #id_file := NULL;
-- save value of id_file column into user-defined variable
UPDATE filenames
SET pending = 2
, id_file = (SELECT #id_file := id_file)
WHERE pending = 1
LIMIT 1;
-- retrieve value stored in user-defined variable
SELECT #id_file;
Note that the value of this variable is maintained within the session. If the UPDATE statement doesn't find any rows that satisfy the predicate (WHERE clause), the value of the user-defined variable will be unaffected... so, to make sure you don't inadvertently get an "old" value, you may want to first initialize that variable with a NULL.
Note that it's important that a subsequently fired trigger doesn't modify the value of that user defined variable. (The user-defined variable is "in scope" in the current session.)
It's also possible to do the assignment to the user-defined variable within in a trigger, but I'm not going to demonstrate that, and I would not recommend you do it in a trigger.

Numbering all rows in MySQL table using loop and cursor

I have a table called "post". It has a column named "ID". It has to be numbered serial-wise normally, but because of some programming requirements, the column gets deranged, and at that time I need to re-number the column again, serial wise. There is no other Primary key.
I tried many methods using Cursor and Looping but to no avail.
Can anyone suggest any method. Please include details.
First you can just delete that column's data fully, tyr to execute following command:-
UPDATE table_name, (select #row_number:= 0) AS t
SET id = (SELECT #row_number := #row_number + 1);
For deleting that column's data you can simply use following command:-
UPDATE tab SET id = NULL;
Hopefully this will solve your problem.

How do I plus one in a table column?

I've got a mysql table column that acts as a counter (int). Each time I update that column I want the field value to get plussed with 1.
So if it was 45 I want it to be 46.
How could I do that with SQL?
You can use a query such as this one :
update your_table
set your_column = your_column + 1
where identifier = X
Of course, up to you to replace the names of the table and the columns ;-)
And to make sure the condition in the where clause is OK ;-)
MySQL v5.0.2 or higher supports triggers, which are pieces of code that are executed, whenever an insert/update/delete operation is made on one of the rows.
for more information about triggers in MySQL check this link
Since you want it to happen each time, you could use a trigger:
delimiter //
create trigger increment_field before update on your_table
for each row
begin
if new.your_column <> old.your_column then
set new.your_column = new.your_column + 1
end if;
end;//
delimiter ;
I am not entirely sure I understand your question. The above solution will make it so whenever a row is updated such that your_column is given a different value, it will instead make your_column = new_value+1. The use of new and old keywords and the conditional used on each row should be changed to suit your needs.

MySQL UPDATE and SELECT in one pass

I have a MySQL table of tasks to perform, each row having parameters for a single task.
There are many worker apps (possibly on different machines), performing tasks in a loop.
The apps access the database using MySQL's native C APIs.
In order to own a task, an app does something like that:
Generate a globally-unique id (for simplicity, let's say it is a number)
UPDATE tasks
SET guid = %d
WHERE guid = 0 LIMIT 1
SELECT params
FROM tasks
WHERE guid = %d
If the last query returns a row, we own it and have the parameters to run
Is there a way to achieve the same effect (i.e. 'own' a row and get its parameters) in a single call to the server?
try like this
UPDATE `lastid` SET `idnum` = (SELECT `id` FROM `history` ORDER BY `id` DESC LIMIT 1);
above code worked for me
You may create a procedure that does it:
CREATE PROCEDURE prc_get_task (in_guid BINARY(16), OUT out_params VARCHAR(200))
BEGIN
DECLARE task_id INT;
SELECT id, out_params
INTO task_id, out_params
FROM tasks
WHERE guid = 0
LIMIT 1
FOR UPDATE;
UPDATE task
SET guid = in_guid
WHERE id = task_id;
END;
BEGIN TRANSACTION;
CALL prc_get_task(#guid, #params);
COMMIT;
If you are looking for a single query then it can't happen. The UPDATE function specifically returns just the number of items that were updated. Similarly, the SELECT function doesn't alter a table, only return values.
Using a procedure will indeed turn it into a single function and it can be handy if locking is a concern for you. If your biggest concern is network traffic (ie: passing too many queries) then use the procedure. If you concern is server overload (ie: the DB is working too hard) then the extra overhead of a procedure could make things worse.
I have the exact same issue. We ended up using PostreSQL instead, and UPDATE ... RETURNING:
The optional RETURNING clause causes UPDATE to compute and return value(s) based on each row actually updated. Any expression using the table's columns, and/or columns of other tables mentioned in FROM, can be computed. The new (post-update) values of the table's columns are used. The syntax of the RETURNING list is identical to that of the output list of SELECT.
Example: UPDATE 'my_table' SET 'status' = 1 WHERE 'status' = 0 LIMIT 1 RETURNING *;
Or, in your case: UPDATE 'tasks' SET 'guid' = %d WHERE 'guid' = 0 LIMIT 1 RETURNING 'params';
Sorry, I know this doesn't answer the question with MySQL, and it might not be easy to just switch to PostgreSQL, but it's the best way we've found to do it. Even 6 years later, MySQL still doesn't support UPDATE ... RETURNING. It might be added at some point in the future, but for now MariaDB only has it for DELETE statements.
Edit: There is a task (low priority) to add UPDATE ... RETURNING support to MariaDB.
I don't know about the single call part, but what you're describing is a lock. Locks are an essential element of relational databases.
I don't know the specifics of locking a row, reading it, and then updating it in MySQL, but with a bit of reading of the mysql lock documentation you could do all kinds of lock-based manipulations.
The postgres documenation of locks has a great example describing exactly what you want to do: lock the table, read the table, modify the table.
UPDATE tasks
SET guid = %d, params = #params := params
WHERE guid = 0 LIMIT 1;
It will return 1 or 0, depending on whether the values were effectively changed.
SELECT #params AS params;
This one just selects the variable from the connection.
From: here