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

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.)

Related

SQL Server filter rows which are being selected in other transactions

i have a couple of jobs Update from select queries e.g
UPDATE TABLE_X
SET "stopFlag" = 1
OUTPUT
INSERTED."RowID" AS "rowID"
WHERE "RowID" IN (
SELECT TOP 50
"RowID"
FROM
TABLE_X
WHERE
stopFlag=0
)
Currently i was thinking that the update cannot conflict with another update but as i see the logs of my database tables it seems that 2 different jobs executed for a single row resulting in messed up data. My question is. Is this a proper way to filter the rows from being selected. If it is then what am i missing?
A transaction is not necessary here, as every statement runs in an auto-commit transaction anyway.
You could up the isolation level to SERIALIZABLE, which may be more consistent, at the cost of more blocking. You could also add an UPDLOCK hint to the inner reference of Table_X.
But I think the best thing to do here will actually improve performance also: don't self-join the table, just update the derived table directly
UPDATE x
SET stopFlag = 1
OUTPUT
inserted.RowID AS rowID
FROM (
SELECT TOP 50
RowID,
stopFlag
FROM
TABLE_X
WHERE
stopFlag = 0
) x;
An UPDLOCK is automatically taken on any rows read from the table reference which is being updated, so that is not necessary.
If you want the statements to run concurrently, but mark and return disjoint rows, use READPAST. You can even introduce ordering guarantees, eg:
UPDATE TABLE_X
SET "stopFlag" = 1
OUTPUT
INSERTED."RowID" AS "rowID"
WHERE "RowID" IN (
SELECT TOP 50
"RowID"
FROM
TABLE_X with (rowlock, updlock, readpast)
WHERE
stopFlag=0
ORDER BY "RowID"
)
See generally Using tables as Queues.

SQL Server : Update by Batch Or Lock Record

I am issuing an UPDATE statement to a list of records in bulk.
There's a trigger in the database table to start firing whenever detect a change of value.
Is there a way for me to hold all the update and complete then release for reading ?
Saw many options here but getting confuse ...
https://learn.microsoft.com/en-us/sql/t-sql/queries/hints-transact-sql-table?view=sql-server-ver15
My SQL Command
UPDATE tbl_table1 SET colStatus = 'Y' WHERE zDate > GETDATE()-1
There could be 10 records, 100 or 1000 or 100,000 records that need to be updated. I know it is fast but I just want to make all records to have same status then allow for reading.
Any thoughts ? Thanks.

Update and then select updated rows?

I have an application that selects row with a particular status and then starts processing these rows. However some long running processing can cause a new instance of my program to be started, selecting the same rows again because it haven't had time to update the status yet. So I'm thinking of selecting my rows and then updating the status to something else so they cannot be selected again. I have done some searching and got the impression that the following should work, but it fails.
UPDATE table SET status = 5 WHERE status in
(SELECT TOP (10) * FROM table WHERE status = 1)
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
TLDR: Is it possible to both select and update rows at the same time? (The order doesn't really matter)
You can use an output clause to update the rows and then return them as if they were selected.
As for updating the top 100 rows only, a safe approach is to use a cte to select the relevant rows first (I assumed that column id can be used to order the rows):
with cte as (select top (100) * from mytable where status = 1 order by id)
update cte set status = 5 output inserted.*
You can directly go for UPDATE statement. It will generate exclusive lock on the row and other concurrent transactions cannot read this row.
More information on locks
Exclusive locks: Exclusive locks are used to lock data being modified by one transaction thus preventing modifications by other
concurrent transactions. You can read data held by exclusive lock only
by specifying a NOLOCK hint or using a read uncommitted isolation
level. Because DML statements first need to read the data they want to
modify you'll always find Exclusive locks accompanied by shared locks
on that same data.
UPDATE TOP(10) table
SET status = 5 WHERE status =1

Non Repeatable Read from database table in SQL Server

Suppose I have a table 100 rows, I just want to select top 10 rows of table, but my situation is that i want to select only those rows which was not previously processed.
For this i have added a Flag column so that i will update whenever i process rows.
But here the problem arises when concurrent request comes for top 10 rows. Both may get same rows and trying to update the same rows (which I dont want to do).
Here I can't use Begin Transaction because It will lock the table and concurrent request will not get handled.
Requirement : My actual requirement is When i am selecting top 10 rows
using flag condition and updating then, then if other request for the
same it will also select other top 10 rows which is not handling by
Request 1.
Example : My table contains 100 rows.
{
Select top 10 * from table_name where flag=0
update table_name set top 10 flag = 1
}
(Will select top 10 out of 100 rows n update)
if at the same time during above request, another request come,
{
Select top 10 * from table_name where flag=0 (Should skip previous request rows)
update table_name set top 10 flag = 1
}
Need: (Will select top 10 out of rest 90 rows n update)
I Need a lock on top 10 rows of first request, but lock should like skip rows of first request even during simultaneous select statement of both requests
Please help me out for this to solve.
You can use an OUTPUT clause to do both the selecting and the updating the flag in one statement, e.g.
UPDATE TOP 10 table
SET flag = 1
WHERE flag = 0
OUTPUT inserted.*
If I understand you correctly you don't want to use a Transaction because it will lock the table for the duration of the update.
Maybe you could split the process into one part which selects the rows and updates the flag and a second part where you actually do your update with the selected rows.
Use a Transaction only for the first part of the task. This will ensure the table is only locked for the absolute Minimum of time.
As for your non-repeatable reads:
If you really want enforce this policy you should delete the selected row from the table and optionally save them to another table where the read-history stays. The lowest-level way to accomplish this guaranteed is with an update of another flag (updated?) and a trigger after the update.
Transaction with ISOLATION LEVEL REPEATABLE READ
{
select top 10 rows
update select-flag
return the 10 rows
}
normal query
{
take the returned 10 rows and do something
change updated-flag
}
Trigger after update if updated-flag changed
{
copy updated to read-history-table
delete updated-rows
}
ISOLATION LEVELS on MSDN
REPEATABLE READ "Specifies that statements cannot read data that has
been modified but not yet committed by other transactions and that
no other transactions can modify data that has been read by the
current transaction until the current transaction completes."

Update Multiple Rows Using An Inequality

I'm trying to update one column in a subset of a table but I can't figure out how to do it in a clean and efficient manner.
Consider the following:
// MyTable
id name flag
0 Steve 0
1 Bob 0
...
10500 Rick 0
I want to change flag to 1 but only for some of the cases. I tried to use
UPDATE MyTable
SET flag = 1
WHERE id <= 500
But obviously that does not work because the subquery returns more than one value. Technically, I could do it like this:
UPDATE MyTable SET flag = 1 WHERE id = 0
UPDATE MyTable SET flag = 1 WHERE id = 1
...
UPDATE MyTable SET flag = 1 WHERE id = 500
But who wants to do it like that? :) Is there a better way for me to format this query and only update those which match an inequality?
EDIT
To clarify exactly what's going on: when I say 'some of these cases' I only mean those which match the inequality, in this case id <= 500
When I run UPDATE MyTable SET flag = 1 WHERE id <= 500 I get the following error:
Subquery returned more than 1 value.
This is not permitted when the subquery follows =, !=, <, <= , >, >=
or when the subquery is used as an expression.
SInce your query does not have a subquery, I would suspect that you have a poorly wrtten trigger on the table that expects only one record at a time to be updated. This needs to be fixed as no trigger should ever be written on this assumption. Triggers in SQL Server need to perform only set-based operations as they work against the whole set not one row at a time.
This is one of the primary use cases for conditional queries (use of a where clause). You look for a way to uniquely identify the rows rows you wish to change and construct a query based on the identifying information. Your initial attempt applies this concept, however, your table as constructed is so general that composing an adequate query to select out only the relevant information is difficult if not impossible. Perhaps try changing the table to make the data more specific preferably by partitioning the data. For example is there some way of grouping the data so that some people can be identified as "students" or "professors" etc? What actually is flag supposed to indicate?
The query you have should work, if it is only that query, it should return the number of rows effected.
The long hand you wrote does exactly what your first query does. What environment are you running it in?