SQL Server - 1 column update, is lock required? - sql-server-2008

I have a need for unique identifiers in my application. To that end, I created a table in my database that only contains 1 column 'unique_id" (BIGINT) and 1 row.
The idea is to use a stored procedure to get the next identifier when I need it. I figured a 1-line operation like this would do the job:
UPDATE identifier_table SET unique_id = unique_id + 1 OUTPUT INSERTED.unique_id
Can someone confirm if this operation is atomic, or do I need to setup a lock on the table?
Thanks!

It is atomic. It is just a single update statement, and will have no problem at all with concurrency since that will be managed by the engine with update locks. You can use OUTPUT as shown, or you can do something like this:
DECLARE #unique_id bigint;
UPDATE identifier_table
SET
#unique_id = unique_id + 1,
unique_id = unique_id + 1;
SELECT #unique_id uniqueid;
If you make #unique_id an OUTPUT parameter, then you can get the value without a select statement or use it easily in another stored procedure.

Related

MySQL Procedure - Assign calculation on columns within an update to a variable to be used in another update

I have a database with 3 tables and I am updating a row in the first, and then trying to update the second and third tables based on the change to a field in the first.
Here is what I have at present:
--Session--
User Hotspot DownloadCounter
bob 123 0
--UserDownload--
User Download
bob 100
--HotspotDownload--
Hotspot Download
123 300
Currently when I get an update from the hotspot I update the session table with the latest traffic counter value, however I would now like to update the UserDownload & HotspotDownload tables with the value of the new DownloadCounter - the old DownloadCounter.
I thought this would be easy with the following procedure:
DECLARE increment INT;
UPDATE Session
SET #increment = (200 - Counter),
Counter = 200
WHERE (User = 'bob' AND Circuit = '123');
UPDATE UserDownload
SET Total = (Total + #increment)
WHERE (User = 'bob');
UPDATE HotspotDownload
SET Total = (Total + #increment)
WHERE (Circuit = '123');
The idea being I update the Session table and assign the counter change value to a variable which is then used in the update of the Download tables. But the '#' causes a syntax error in the procedure, and without the '#' it thinks increment is a column so fails.
If there are not entries for the user/hotspot in the total tables I still need to update the sessions table.
I tried doing this with a single update and a left join, but that resulted in the Sessions table column being updated first and so then the Download tables would not be updated.
Is it possible to assign the result of a calculation on columns to a variable within an update, and then use that update within another update in the procedure?
Thanks.
OK, looks like the way to do this is triggers.
Have the procedure just update the Session table, and then have a trigger against the table which runs before the actual update and updates the 2nd and 3rd tables.

How to generate Sequential User Code using MySQL Trigger?

I have created a MySQL Trigger BEFORE INSERT on table name agent_mst as below
BEGIN
DECLARE max_id INT;
SET max_id=(SELECT MAX(agent_id_pk)+1 FROM `agent_mst`);
IF (max_id IS NULL) THEN
SET max_id=1;
END IF;
SET NEW.date_added=NOW(),
NEW.date_updated=NOW(),
NEW.agent_code = CONCAT('SDA', LPAD(max_id, 4,'0'));
END
So what it does is, every time we inset a record, it generates agent_code field value to something like SDA0001, SDA0002, SDA0003, ...
Now suppose I delete a record with code SDA0003 and insert new record, it will definitely generate the agent code as SDA0004. As it is taking the max_id and increasing it with 1. But here I want to get SDA0003 again. So that all agent_codes can stay in sequence. How to do that?
Thanks in advance.
you need to identify the first (smallest) missing id.
check out in this link, a nice way to do it in a select query:
Find mininum not used value in mysql table
To know next auto increment id try to run below query and check column "Auto_increment":
SHOW TABLE STATUS FROM DBName where name = 'tableName'

How to select and update a record at the same time in mySQL?

Is there any way to select a record and update it in a single query?
I tried this:
UPDATE arrc_Voucher
SET ActivatedDT = now()
WHERE (SELECT VoucherNbr, VoucherID
FROM arrc_Voucher
WHERE ActivatedDT IS NULL
AND BalanceInit IS NULL
AND TypeFlag = 'V'
LIMIT 1 )
which I hoped would run the select query and grab the first record that matches the where clause, the update the ActivatedDT field in that record, but I got the following error:
1241 - Operand should contain 1 column(s)
Any ideas?
How about:
UPDATE arrc_Voucher
SET ActivatedDT = NOW()
WHERE ActivatedDT IS NULL
AND BalanceInit IS NULL
AND TypeFlag = 'V'
LIMIT 1;
From the MySQL API documentation :
UPDATE returns the number of rows that were actually changed
You cannot select a row and update it at the same time, you will need to perform two queries to achieve it; fetch your record, then update it.
If you are worrying about concurrent processes accessing the same row through some kind of race condition (supposing your use case involve high traffic), you may consider other alternatives such as locking the table (note that other processes will need to recover--retry--if the table is locked while accessing it)
Or if you can create stored procedure, you may want to read this article or the MySQL API documentation.
But about 99% of the time, this is not necessary and the two queries will execute without any problem.

SQL (mySQL) update some value in all records processed by a select

I am using mySQL from their C API, but that shouldn't be relevant.
My code must process records from a table that match some criteria, and then update the said records to flag them as processed. The lines in the table are modified/inserted/deleted by another process I don't control. I am afraid in the following, the UPDATE might flag some records erroneously since the set of records matching might have changed between step 1 and step 3.
SELECT * FROM myTable WHERE <CONDITION>; # step 1
<iterate over the selected set of lines. This may take some time.> # step 2
UPDATE myTable SET processed=1 WHERE <CONDITION> # step 3
What's the smart way to ensure that the UPDATE updates all the lines processed, and only them? A transaction doesn't seem to fit the bill as it doesn't provide isolation of that sort: a recently modified record not in the originally selected set might still be targeted by the UPDATE statement. For the same reason, SELECT ... FOR UPDATE doesn't seem to help, though it sounds promising :-)
The only way I can see is to use a temporary table to memorize the set of rows to be processed, doing something like:
CREATE TEMPORARY TABLE workOrder (jobId INT(11));
INSERT INTO workOrder SELECT myID as jobId FROM myTable WHERE <CONDITION>;
SELECT * FROM myTable WHERE myID IN (SELECT * FROM workOrder);
<iterate over the selected set of lines. This may take some time.>
UPDATE myTable SET processed=1 WHERE myID IN (SELECT * FROM workOrder);
DROP TABLE workOrder;
But this seems wasteful and not very efficient.
Is there anything smarter?
Many thanks from a SQL newbie.
There are several options:
You could lock the table
You could add an AND foo_id IN (all_the_ids_you_processed) as the update condition.
you could update before selecting and then only selecting the updated rows (i.e. by processing date)
I eventually solved this issue by using a column in that table that flags lines according to their status. This column let's me implement a simple state machine. Conceptually, I have two possible values for this status:
kNoProcessingPlanned = 0; #default "idle" value
kProcessingUnderWay = 1;
Now my algorithm does something like this:
UPDATE myTable SET status=kProcessingUnderWay WHERE <CONDITION>; # step 0
SELECT * FROM myTable WHERE status=kProcessingUnderWay; # step 1
<iterate over the selected set of lines. This may take some time.> # step 2
UPDATE myTable SET processed=1, status=kNoProcessingPlanned WHERE status=kProcessingUnderWay # step 3
This idea of having rows in several states can be extended to as many states as needed.

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