My question is: What are the circumstances which make NOLOCK risky?
large rows with enough data that it can cross a page. This requires a large number of columns or varchar(max) nvarchar(max) varbinary(max) xml text ntext or image columns.
A non clustered index PK lookup.
The row was involved in a transaction which was subsequently rolled back.
database maintenance can still cause data movement and errors
If you have a variable width column and you expand it with an update, it could get moved to a new page. Anything that causes data to move to a new page can be a problem. Data movement between pages can cause a "Error 601: Could not continue scan with NOLOCK due to SQL Server data movement. [3]
1 and 2 can be found here "torn reads"
Are there any other circumstances which can cause partial/incorrect rows returned?
Firstly:
No, you can get that even on a single one of those columns, where the data is stored off-row, so any case where it's more than 8kb.
No, you can get problems even if the transaction wasn't rolled back. If you have a page split, or the row is moved in the index sort to a different page), then the row could have been read twice, or not at all, or different each time.
And it doesn't have to be that row, it just needs to be on a page which is being changed. It could even be an index or table rebuild.
Other possible problems:
BULK_OPERATIONlock, which may cause more blocking on other queries.Sch-Mschema modification lock ongoing.INNER JOINquery, where that row is expected to exist uniquely exactly once? You would double up your entire resultset, or zero out the entire resultset, because of the join condition.Bottom line, there is almost never a reason to use
NOLOCK.If you want performance and think no-one is changing the table anyway, then just use
WITH (TABLOCK)and be done.Or if you are sure no-one is ever going to change anything in your database then set it to
READ_ONLYand no locks will get taken in the first place.And if you are worried about blocking then use
SNAPSHOTorREAD COMMITTED SNAPSHOTisolation and avoid the whole issue.The only time you should ever use it is in either diagnostic queries (to investigate blocking or running queries), or when querying system tables. And if you really do need to use it, then just set the isolation level explicitly.