I want to update a single record in SQL Server 2016 with 2000+ concurrent request with less duration - sql-server-2016

I am using SQL Server 2016, I have a table table1 with 2 columns name and value. Basically it's like a counter - every time I will update with increment of one for every request.
And the query I am using for the update is:
DECLARE #counter INT;
SET #counter = '124';
UPDATE table1
SET Value = #counter
WHERE Name = 'requestCount'
When I am running with 2000+ concurrent requests, it's getting slower response, on average the duration is 3476 ms.
Requirement: average duration shold be less than 100 ms. Is this possible? if yes how can we achieve this?
Thank in advance

Related

T-SQL ways to avoid potentialy updating the same row based on subquery results

I have a SQL Server table with records (raw emails) that needs to be processed (build the email and send it) in a given order by an external process (mailer). Its not very resource intensive but can take a while with all the parsing and SMTP overhead etc.
To speed things up I can easily run multiple instance of the mailer process over multiple servers but worry that if two were to start at almost the same time they might still overlap a bit and send the same records.
Simplified for the question my table look something like this with each record having the data for the email.
queueItem
======================
queueItemID PK
...data...
processed bit
priority int
queuedStart datetime
rowLockName varchar
rowLockDate datetime
Batch 1 (Server 1)
starts at 12:00PM
lock/reserve the first 5000 rows (1-5000)
select the newly reserved rows
begin work
Batch 2 (Server 2)
starts at 12:15PM
lock/reserve the next 5000 rows (5001-10000)
select the newly reserved rows
begin work
To lock the rows I have been using the following:
declare #lockName varchar(36)
set #lockName = newid()
declare #batchsize int
set #batchsize = 5000
update queueItem
set rowLockName = #lockName,
rowLockDate = getdate()
where queueitemID in (
select top(#batchsize) queueitemID
from queueItem
where processed = 0
and rowLockName is null
and queuedStart <= getdate()
order by priority, queueitemID
)
If I'm not mistaken the query would start executing the SELECT subquery first and then lock the rows in preparation of the update, this is fast but not instantaneous.
My concern is that if I start two batches at near the same time (faster than the subquery runs) Batch 1's UPDATE might not be completed and Batch 2's SELECT would see the records as still available and attempt/succeed in overwriting Batch 1 (sort of race condition?)
I have ran some test but so far haven't had the issue with them overlapping, is it a valid concern that will come to haunt me at the worst of time?
Perhaps there are better way to write this query worth looking into as I am by no mean a T-SQL guru.

SQL Server Update TOP by date entered

I have a table [occ] without PK or timestamps.
These are the columns
on_date type_id price_id processed
I need to update 50 records to processed = 1 every X minutes.
My issue is that the code below acts a bit randomly and may update newer records and leave out older ones, creating huge sync problems in my backend
UPDATE TOP (50) occ
SET processed = 1
FROM occ
How could I make sure that my TOP 50 updated records are the oldest evey time?
Thank you
By adding Where and Order By clause like this - Order By on_date in your query.
UPDATE TOP (50) occ
SET processed = 1
FROM occ
WHERE processed = 0
Order By on_date
The answer is that unless you store the timestamp or a sequential ID (such as an identity field) - you can not guarantee that TOP 50 returns the the oldest 50 records. SQL might seem to often return the earliest records first in such cases, but this is not a guaranteed or predictable behaviour.
Create one ID(Identity Column) for every row added in the table and and use Order By Id in your Query
UPDATE TOP (50) occ
SET processed = 1
FROM occ
WHERE processed = 0
Order By Id

CURSOR vs. UPDATE

A company uses a SQL Server database to store information about its customers and its business transactions. A new area code has been introduced for your city. The area code 111 remains the same for telephone numbers with prefixes that are less than 500. The numbers with prefixes that are 500 and greater will be assigned an area code of 222. All telephone numbers in the Phone column in the Customers table are stored as char(12) strings of the following format, ‘999-999-9999’. i must make the appropriate changes to the Customers table
as quickly as possible using the least administrative effort. Which one should I use ?
a.
UPDATE Customers SET Phone = ‘222-‘ + SUBSTRING(Phone,5,8)
FROM Customers WHERE SUBSTRING(Phone,1,3) = ‘111’
AND SUBSTRING(Phone,5,3) >= 500
b.
DECLARE PhoneCursor CURSOR FOR
SELECT Phone FROM Customers
WHERE SUBSTRING(Phone,1,3) = 111
AND SUBSTRING(Phone,5,3) >= 500
OPEN PhoneCursor
FETCH NEXT FROM PhoneCursor
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE Customers
SET Phone = ‘222’ + SUBSTRING(Phone,5,8)
WHERE CURRENT OF PhoneCursor
FETCH NEXT FROM PhoneCursor
END
CLOSE PhoneCursor
DEALLOCATE PhoneCursor
The big update will hold a transaction against the database for, potentially, a long time... locking things up and causing all kinds of havoc.
For this, I would recommend a cursor to spread that load out over a period of time.
I've also done a 'chunked' update... something like this:
DECLARE #Done bit = 0
WHILE #Done = 0
BEGIN
UPDATE TOP(10000)
Customers SET Phone = ‘222-‘ + SUBSTRING(Phone,5,8)
FROM Customers WHERE SUBSTRING(Phone,1,3) = ‘111’
AND SUBSTRING(Phone,5,3) >= 500
IF ##ROWCOUNT = 0
BEGIN
SET #Done = 1
END
END
The cursor would be far slower in any large dataset, talk about hours vice seconds or milliseconds. What it will do is not lock up the database from other users for as long at any one time.
This is, why with a large dataset, the batch approach can be best.
In general I would try the set-based approach first and run it on off hours if need be. Then I would try the set-based approach which runs a batch of the records only if the set-based is too slow.
If you have to go the cursor running one record at time, then there is probably something massively wrong with your database design. Cursors are generally the approach of last resort. Do not use them for inserts/updates or deletes.

SQL: Updating a table updates only 7 rows at a time

While executing a SQL update,
update request set state_id = 2 where id in (
select id from request where state_id = 0 and entity_id in
(<list of numbers>)
)
This updates only 7 rows at a time.
The inner select query,
select id from request where state_id = 0 and entity_id in (<list of numbers>)
returns 2000+ records.
I am using Squirrel SQL client to run the query and checked if the limit rows is set to 3000.
What makes it more interesting is, if I wait for more time in between successive executing of the update query, more rows gets updated.
I mean, When I waited for 10 secs, and executed the update, 45 rows got updated.
but when ran rapidly, just 7 rows gets updated.
Can someone please help me point out what I might be doing wrong?
Executing the inner select -
Executing the update -
I suggest writing a proper update, since I see no reason to introduce a more complex query involving a subquery. Also just to be sure you didn't previously alter ##ROWCOUNT:
SET ##ROWCOUNT 0;
UPDATE dbo.request
SET state_id = 2
WHERE state_id = 0
AND entity_id IN (<list of numbers>);
Also check that you haven't artificially limited the row count in Management Studio. In Tools > Options:
(Well, that would have been relevant if you had tagged correctly. Check whatever client tool you use against Sybase and see if it has some similar option.)

sql table cell modified by multiple threads at the same time

if you have table BankAccount that has a column Amount and the value for this column for a specific row can be modified by multiple threads at the same time so it could happen so that the last one to set the value will win.
how do you usually handle this kind of situations ?
UPDATE: I heard that in MSSQL there is update lock UPDLOCK that locks the table or the row that is being updated, could I this here somehow ?
An update statement which references the current value would prevent overwriting. So, instead of doing something like
SELECT Amount FROM BankAccount WHERE account_id = 1
(it comes back as 350 and you want to subtract 50)...
UPDATE BankAccount SET Amount = 300 WHERE account_id = 1
do
UPDATE BankAccount SET Amount = Amount - 50 WHERE account_id = 1
You cannot have several threads modifying the same data at exactly the same time : it will always be the last one which set the value that'll "win".
If the problem is that several threads read and set the value at almost the same time, and reads and writes don't arrive on the right order, the solution is to use Transactions :
start a transaction
read the value
set the new value
commit the transaction
This ensures the read and the write will be done consistently, and no other thread will be able to modify the data during the same transaction.
Quoting the wikipedia page about Database Transactions :
A database transaction comprises a
unit of work performed within a
database management system (or similar
system) against a database, and
treated in a coherent and reliable way
independent of other transactions.
Transactions in a database environment
have two main purposes:
To provide reliable units of work that allow correct recovery from
failures and keep a database
consistent even in cases of system
failure, when execution stops
(completely or partially) and many
operations upon a database remain
uncompleted, with unclear status.
To provide isolation between programs accessing a database
concurrently. Without isolation the
programs' outcomes are typically
erroneous.
You ussually use transactions to overcome this.
Have a look at Database transaction
You should have a database function/procedure which makes operations with the "Amount". This function/procedure should return if the operation was succeeded or failed (for example, you want take $1000, but current AMount is only $550, so operation can not be proceede).
Expamle in T-SQL:
UPDATE BankAccount SET Amount = Amount - 1000 WHERE BankAcountID = 12345 AND Amount >= 1000
RETURN ##ROWCOUNT
If the amaount was changed, the return value will be 1, otherwise 0.
Know, you can safely run this functions/procedures (in several threads too):
DECLARE #Result_01 int, Result_02 int, Result_03 int
EXEC #Result_01 = ChangeBankAccountAmount #BankAcountID = 12345, #Amount = 1000
EXEC #Result_02 = ChangeBankAccountAmount #BankAcountID = 12345, #Amount = 15
EXEC #Result_03 = ChangeBankAccountAmount #BankAcountID = 12345, #Amount = 600, #Amount = -2000
EDIT:
Whole procedure in T-SQL:
CRATE PROC ChangeBankAccountAmount
#BankAccountID int,
#ChangeAmount int,
#MMinAmount int = 0
AS BEGIN
IF #ChangeAmount >= 0
UPDATE BankAccount SET Amount = Amount + #ChangeAmount WHERE BankAcountID = 12345
ELSE
UPDATE BankAccount SET Amount = Amount + #ChangeAmount WHERE BankAcountID = 12345 AND Amount >= #MMinAmount
RETURN ##ROWCOUNT
END
Of course - the "int" datatype is not good for money, you should change it to datatype used in your table.