Atomic update column in SQL Server - sql

I am using MS SQL Server, and I have a table. I want each SQL Server client to acquire one row and update it, so that the client knows that only he acquired that record, and no one else!
record1 a b c 0
record2 d e f 0
...
One client should acquire and update last value from 0 to 1.
The solution should work at least from SQL Server 2005 and above.
ATOMIC
{
select top 1 * from table where column='0' // get one row where column is '0'
update table set column='1'
}

You could do something like:
update top (1) table with (readpast)
set Column1=1
output inserted.*
where Column1 = 0
Which should do it all in one go. You only need the readpast hint if, having acquired the unique value, the connection keeps a transaction open for a long period of time. If you're just doing the update you can omit it.

You can update a table through a Common Table Expression.
Below I create a CTE that picks just one row and then that is used in the UPDATE.
WITH
singleRow AS
(
select top 1 * from table where column='0'
)
UPDATE
singleRow
SET
column= '1'
;
NOTE: Remember to ensure any preceding commands are terminated with a ;, else the WITH syntax doesn't work.

Related

Deleting two rows based on successful deletion of another row which is based on a column condition - Sybase

I am trying to delete two rows based on only if another 3rd row is deleted. Now, the catch here is, the 3rd row has a condition where only for value "A" of column "MAN", it should delete that corresponding row.
i am doing it in Sybase (SQL Anywhere using Interactive SQL IDE).
I am unable to use ##ROWCOUNT.
Could someone please give me the query for this or how to achieve this please.
ALGORITHM:
DELETE FROM EFS
WHERE NAME='MAN' AND VAL='A'
If (DeleteSuccessful)
{
DELETE FROM EFS
WHERE NAME='MAN_1' AND NAME='MAN_2'
}
I want to achieve this in a single query in Sybase.
ALTERNATIVE APPROACH:
I can also think of achieve this by first checking the value of VAL column and if it is XX, then I can write a query to delete all 3 rows using WHERE NAME ='MAN AND NAME ='MAN_1' AND NAME ='MAN_2'. this is also one approach. But don't know how to do it using syntax in Sybase in a single query/
I think you should check if these values exists and do it in one query
DELETE FROM EFS
WHERE (NAME='MAN' AND VAL='A')
OR
(
(NAME='MAN_1' AND NAME='MAN_2')
AND
EXISTS (SELECT * FROM EFS WHERE NAME='MAN' AND VAL='A')
)
I believe both you and valex are both getting the correct result, but your syntax is a little off. Also better to use IN vs. OR for performance.
IF EXISTS (SELECT * FROM EFS WHERE NAME='MAN' AND VAL='A')
BEGIN
DELETE FROM EFS WHERE NAME IN ('MAN','MAN_2','MAN_3')
END
And, I think you can use ##rowcount, perhaps you don't consider this a single statement approach but it also works, e.g.
select * from tempdb..test
irecord
1
2
3
99
declare #deleted INT
delete from tempdb..test where irecord = 3
select #deleted = ##rowcount
if #deleted > 0
begin
print 'deletion detected'
delete from tempdb..test where irecord IN (1,2)
end
else
print 'no deletion detected'
1 row(s) affected.
1 row(s) affected.
deletion detected
2 row(s) affected.
select * from tempdb..test
irecord
99

SQL Error [512] [21000]: Subquery returned more than 1 value

Why does it not work? (SQL Server)
UPDATE
someTable
SET
name='AB'
WHERE
id IN (
SELECT t.id
FROM someTable t
WHERE t.name='ABC'
)
this one doesn't work too
UPDATE
someTable
SET
name='AB'
WHERE
name='ABC'
Because you must have a broken UPDATE trigger on the table.
A common error in triggers is not taking into account that a statement can affect multiple or zero rows and thus that the INSERTED/DELETED tables don't always contain exactly one row.
Look in the trigger for constructs like
SET #ID = (select ID FROM INSERTED)

How to update exactly one record and select updated row in a single db2 query

I want to write a query which updates only one row of a table then returns updated rows.
I can achieve getting returned rows using
select field from final table
(update tablename set anotherfield = 'dd' where someanotherfield = 'bb')
kind of statement. But i cannot update just one row.
Also my program that calls this query is a multithreaded one and i dont want to deadlock any processes so, i found SKIP LOCKED DATA statement which is like readpast in t-sql query.
So what i am trying to do is, my program uses an db2 table like a stack, selects only one row each time, updates it so any other thread cannot access it but does not make them wait
entire table, they just skip updated row and select next record from table. Is this operation possible in a single db2 query?
I have written this query so far, and it cannot be processed
select COLUMN3 FROM FINAL TABLE
(
update MYTABLE
set COLUMN1 = 'R'
where COLUMN1 = ''
order by COLUMN2
FETCH FIRST 1 ROW ONLY
SKIP LOCKED DATA
)
Any help would be appreciated,
Thanks.
Assuming that COLUMN2 is a unique_id , you could do something like this:
select COLUMN3 FROM FINAL TABLE
(
update MYTABLE
set COLUMN1 = 'R'
where COLUMN1 = ''
AND COLUMN2 = ( SELECT COLUMN2
FROM MYTABLE
ORDER by COLUMN2
FETCH FIRST 1 ROW ONLY)
SKIP LOCKED DATA
)
You were doing an order by inside your update and that doesn't work. You have to restrain that to only one result (likely using your primary key).

select the rows affected by an update

If I have a table with this fields:
int:id_account
int:session
string:password
Now for a login statement I run this sql UPDATE command:
UPDATE tbl_name
SET session = session + 1
WHERE id_account = 17 AND password = 'apple'
Then I check if a row was affected, and if one indeed was affected I know that the password was correct.
Next what I want to do is retrieve all the info of this affected row so I'll have the rest of the fields info.
I can use a simple SELECT statement but I'm sure I'm missing something here, there must be a neater way you gurus know, and going to tell me about (:
Besides it bothered me since the first login sql statement I ever written.
Is there any performance-wise way to combine a SELECT into an UPDATE if the UPDATE did update a row?
Or am I better leaving it simple with two statements? Atomicity isn't needed, so I might better stay away from table locks for example, no?
You should use the same WHERE statement for SELECT. It will return the modified rows, because your UPDATE did not change any columns used for lookup:
UPDATE tbl_name
SET session = session + 1
WHERE id_account = 17 AND password = 'apple';
SELECT *
FROM tbl_name
WHERE id_account = 17 AND password = 'apple';
An advice: never store passwords as plain text! Use a hash function, like this:
MD5('apple')
There is ROW_COUNT() (do read about details in the docs).
Following up by SQL is ok and simple (which is always good), but it might unnecessary stress the system.
This won't work for statements such as...
Update Table
Set Value = 'Something Else'
Where Value is Null
Select Value From Table
Where Value is Null
You would have changed the value with the update and would be unable to recover the affected records unless you stored them beforehand.
Select * Into #TempTable
From Table
Where Value is Null
Update Table
Set Value = 'Something Else'
Where Value is Null
Select Value, UniqueValue
From #TempTable TT
Join Table T
TT.UniqueValue = T.UniqueValue
If you're lucky, you may be able to join the temp table's records to a unique field within Table to verify the update. This is just one small example of why it is important to enumerate records.
You can get the effected rows by just using ##RowCount..
select top (Select ##RowCount) * from YourTable order by 1 desc

SQL Server 2005 and SELECT and UPDATE locked

I want to perform a update then select the result. I don't want anything to be able to update the row I am updating until after the select has occurred. How would I do this?
My goal is to increment a value of a row and return that incremented value. I have thus far found that I end up with an issue where update (to increment) followed by a select in a situation where two queries happen at near the same time the selects seem to return the same number. So I am guessing that something like update > update > select > select is happening.
I miss labeled this as SQL Server 2005. I am actually working with Server 2000. So the output clause does not work (is not in that version).
BEGIN TRANSACTION
UPDATE Table SET Last=(Last+1) WHERE ID=someid;
SELECT * FROM Table WHERE ID=someid;
COMMIT TRANSACTION
BEGIN TRAN
UPDATE ...
SELECT...
COMMIT
Should do it even at the default transaction isolation level of read committed.
You could also use the OUTPUT clause to get the row directly back after the update. Example of this
UPDATE <YourTable>
SET ...
OUTPUT INSERTED.*
WHERE ...