I am trying to put an with(NOLOCK) on an update query:
UPDATE pth_patchLookup with(nolock) SET ScanDateTime = Getdate() WHERE RegID = 312
but I get the following message :
NoLock hint is supported only with Select statement and not with update, insert and delete.
Is there any manner in which I can apply a 'NOLOCK" on this update query ?
Thanks for any help
(NOLOCK) disables shared locks and not exclusive locks.
you can use Read Committed isolation level in order to place an exclusive lock on select statements.
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
UPDATE pth_patchLookup SET ScanDateTime = Getdate() WHERE RegID = 312
And this?
UPDATE TOP (1000) v
SET idSupervisor = a.supervisor
FROM dbo.Venda_2014 v WITH ( NOLOCK )
INNER JOIN #supervidores_presentes a WITH (NOLOCK) ON v.IdVendedor = a.vendedor AND v.idEmpresaOriginal = a.empresa
WHERE IdDistribuidor IN ( 40 )
AND ISNULL(v.idSupervisor,0) = 0
AND datAnoMesRef >= '201501'
Go
Working fine for me.
NOLOCK is a select (only) hint and it's much a bad habit form older programmers since it was almost mandatory in SQL Server 7 but since SQL Server 2000 it's most unnecessary.
That hint in particular tell the engine the select can read rows even if it is in the middle of a uncommited transaction. Due to this you can experience dirty or ghost reads.
I strongly suggest you to read about isolation levels to know how to meet your particular system requirements.
Obs: Hints are not commands, you are only suggesting the engine to take a hint but the engine can decide to not use it.
Related
According to the UPDATE documentation, an UPDATE always acquires an exclusive lock on the whole table. However, I am wondering if the exclusive lock is acquired before the rows to be updated are determined or only just before the actual update.
My concrete problem is that I have a nested SELECT in my UPDATE like this:
UPDATE Tasks
SET Status = 'Active'
WHERE Id = (SELECT TOP 1 Id
FROM Tasks
WHERE Type = 1
AND (SELECT COUNT(*)
FROM Tasks
WHERE Status = 'Active') = 0
ORDER BY Id)
Now I am wondering whether it is really guaranteed that there is exactly one
task with Status = 'Active' afterwards if in parallel the same statement may be executed with another Type:
UPDATE Tasks
SET Status = 'Active'
WHERE Id = (SELECT TOP 1 Id
FROM Tasks
WHERE Type = 2 -- <== The only difference
AND (SELECT COUNT(*)
FROM Tasks
WHERE Status = 'Active') = 0
ORDER BY Id)
If for both statements the rows to change would be determined before the lock is acquired, I could end up with two active tasks which I must prevent.
If this is the case, how can I prevent it? Can I prevent it without setting the transaction level to SERIALIZABLE or messing with lock hints?
From the answer to Is a single SQL Server statement atomic and consistent? I learned that the problem arises when the nested SELECT accesses another table. However, I'm not sure if I have to care about this issue if only the updated table is concerned.
If you want exactly one task with static = active, then set up the table to ensure this is true. Use a filtered unique index:
create unique index unq_tasks_status_filter_active on tasks(status)
where status = 'Active';
A second concurrent update might fail, but you will be ensured of uniqueness. Your application code can process such failed updates, and re-try.
Relying on the actual execution plans of the updates might be dangerous. That is why it is safer to have the database do such validations. Underlying implementation details could vary, depending on the environment and version of SQL Server. For instance, what works in a single threaded, single processor environment may not work in a parallel environment. What works with one isolation level may not work with another.
EDIT:
And, I cannot resist. For efficiency purposes, consider writing the query as:
UPDATE Tasks
SET Status = 'Active'
WHERE NOT EXISTS (SELECT 1
FROM Tasks
WHERE Status = 'Active'
) AND
Id = (SELECT TOP 1 Id
FROM Tasks
WHERE Type = 2 -- <== The only difference
ORDER BY Id
);
Then place indexes on Tasks(Status) and Tasks(Type, Id). In fact, with the right query, you might find that the query is so fast (despite the update on the index) that your worry about current updates is greatly mitigated. This would not solve a race condition, but it might at least make it rare.
And if you are capturing errors, then with the unique filtered index, you could just do:
UPDATE Tasks
SET Status = 'Active'
WHERE Id = (SELECT TOP 1 Id
FROM Tasks
WHERE Type = 2 -- <== The only difference
ORDER BY Id
);
This will return an error if a row already is active.
Note: all these queries and concepts can be applied to "one active per group". This answer is addressing the question that you asked. If you have a "one active per group" problem, then consider asking another question.
This not an answer on your question... But your query is pain for my eyes :)
;WITH cte AS
(
SELECT *, RowNum = ROW_NUMBER() OVER (PARTITION BY [type] ORDER BY id)
FROM Tasks
)
UPDATE cte
SET [Status] = 'Active'
WHERE RowNum = 1
AND [type] = 1
AND NOT EXISTS(
SELECT 1
FROM Tasks
WHERE [Status] = 'Active'
)
No, at least the nested select statement can be processed before the update is started and locks are acquired. To make sure that no other query interferes with this update it is required to set the transaction isolation level to SERIALIZABLE.
This article (and the series it is part of) explains very well the subtleties of concurrency in SQL server:
http://sqlperformance.com/2014/02/t-sql-queries/confusion-caused-by-trusting-acid
I use SQL Server 2012.
I write two queries but what is a different between NOLOCK and UnCommitted ?
SELECT lastname, firstname
FROM HR.Employees with (READUNCOMMITTED)
SELECT lastname, firstname
FROM HR.Employees with (NoLock)
NOLOCK : Is equivalent to READ UNCOMMITTED (source : MSDN)
NOLOCK or READ UNCOMMITTED Specifies that dirty reads are allowed. No shared locks are issued to prevent other transactions from modifying data read by the current transaction, and exclusive locks set by other transactions do not block the current transaction from reading the locked data. Allowing dirty reads can cause higher concurrency, but at the cost of reading data modifications that then are rolled back by other transactions
READ UNCOMMITTED and NOLOCK hints apply only to data locks. All queries, including those with READ UNCOMMITTED and NOLOCK hints, acquire Sch-S (schema stability) locks during compilation and execution. Because of this, queries are blocked when a concurrent transaction holds a Sch-M (schema modification) lock on the table
No difference in terms of their functions, like other have mentioned.
The single difference is that you can apply WITH(NOLOCK) selectively, on some tables but not others. READ UNCOMMITTED applies NOLOCK to all tables in a session.
If you do this:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT *
FROM Table1 T1
INNER JOIN Table2 T2 ON T1.ID = T2.id
It is functionally equivalent to:
SELECT *
FROM Table1 T1 WITH(NOLOCK)
INNER JOIN Table2 T2 WITH(NOLOCK) ON T1.ID = T2.ID
But you can also apply WITH(NOLOCK) selectively:
SELECT *
FROM Table1 T1 WITH(TABLOCK)
INNER JOIN Table2 WITH(NOLOCK) ON T1.ID = T2.ID
Under the hood they are the performing the same action.
The READ UNCOMMITTED isolation level is the least restrictive isolation level within SQL Server, which is also what makes it popular for developers when looking to reduce blocking.
The NOLOCK table hint behind the scenes performs the exact same action as running under the read-uncommitted isolation level.
The only difference between the two is that the READ UNCOMMITTED isolation level determines the locking mechanism for the entire connection and the NOLOCK table hint determines the locking mechanism for the table that you give the hint to.
There is no difference at the statement level.
You can set READUNCOMMITED at the session level and here you have to write
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
For NOLOCK , we need to put this hint on table level, so it is require to put for every tables level which are used in update transaction. So it is very lengthy and time consuming to put it everywhere tables refers in query. For READ UNCOMMITTED, We do not need to put it every tables level, just put at session level or query level and can be written at top of the query or stored procedure.
Let us look on small demo to elaborate it. First checking here database default isolation level
CREATE TABLE SAMPLETABLE
(
Col1 INT ,
Col2 VARCHAR(100)
)
INSERT INTO SAMPLETABLE(Col1,Col2)
SELECT 1,'Col1'
Union all
SELECT 2,'Col1'
BEGIN TRANSACTION
Update SAMPLETABLE Set Col2 = 'Value changed' Where col1 =1
Select * from SAMPLETABLE with (nolock)
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
Select * from SAMPLETABLE
Output is 1, Col1 for both query
Am trying to update the isdeleted column in one table when a record is not in the other user table . My problem is the query l have written runs forever. how best can l write the query below.
update TBLG2O_REGISTER a set a."isDeleted" = '1'
where a."UserID" not in (select k."UserID" from TBLG2O_USER k)
The answer is going to be database engine-specific. Performance characteristic differ wildly, across different database engines, and you failed to specify which DB server you are using.
However, subqueries are frequently MySQL's Achilles heel; I wouldn't be surprised that if this was MySQL. If so, the following approach should have better performance characteristics with MySQL:
update TBLG2O_REGISTER a left join TBLG20_USER k using(UserID)
set a.isDeleted = '1' where k.UserID is null;
Finally got it to work Thank you for your help
Update TBLG2O_REGISTER a set a."isDeleted" = '1' where a."UserID" in (select p."UserID"
from TBLG2O_REGISTER p left join TBLG2O_USER k on p."UserID" =k."UserID"
where k."UserID" is null)
In the Following query where would I place WITH(NOLOCK)?
SELECT *
FROM (SELECT *
FROM (SELECT *
FROM (SELECT *
FROM (SELECT *
FROM dbo.VBsplit(#mnemonicList, ',')) a) b
JOIN dct
ON dct.concept = b.concept
WHERE b.geo = dct.geo) c
JOIN dct_rel z
ON c.db_int = z.db_int) d
JOIN rel_d y
ON y.rel_id = d.rel_id
WHERE y.update_status = 0
GROUP BY y.rel_id,
d.concept,
d.geo_rfa
You should not put NOLOCK anywhere in that query. If you are trying to prevent readers from blocking writers, a much better alternative is READ COMMITTED SNAPSHOT. Of course, you should read about this, just like you should read about NOLOCK before blindly throwing it into your queries:
Is the NOLOCK SQL Server hint bad practice?
Is NOLOCK always bad?
What risks are there if we enable read committed snapshot in SQL Server?
Also, since you're using SQL Server 2008, you should probably replace your VBSplit() function with a table-valued parameter - this will be much more efficient than splitting up a string, even if the function is baked in CLR as implied.
First, create a table type that can hold appropriate strings. I'm going to assume the list is guaranteed to be unique and no individual mnemonic word can be > 900 characters.
CREATE TYPE dbo.Strings AS TABLE(Word NVARCHAR(900) PRIMARY KEY);
Now, you can create a procedure that takes a parameter of this type, and which sets the isolation level of your choosing in one location:
CREATE PROCEDURE dbo.Whatever
#Strings dbo.Strings READONLY
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL --<choose wisely>;
SELECT -- please list your columns here instead of *
FROM #Strings AS s
INNER JOIN dbo.dct -- please always use proper schema prefix
ON dct.concept = s.Word
...
END
GO
Now you can simply pass a collection (such as a DataTable) in from your app, be it C# or whatever, and not have to assemble or deconstruct a messy comma-separated list at all.
Since the question really is, "where should I put NOLOCK". I am not going do debate the use of OR reformat the query with better joins. I will just answer the question.
In no way am I intending to say this is the better way or to say that the other answers are bad. The other answer solve the actual problem. I'm just intending to show where exactly to place the lock hints as the question asks
SELECT *
FROM (SELECT *
FROM (SELECT *
FROM (SELECT *
FROM (SELECT *
FROM dbo.VBsplit(#mnemonicList, ',')) a) b
JOIN dct WITH (NOLOCK) -- <---
ON dct.concept = b.concept
WHERE b.geo = dct.geo) c
JOIN dct_rel z WITH (NOLOCK) -- <---
ON c.db_int = z.db_int) d
JOIN rel_d y WITH (NOLOCK) -- <---
ON y.rel_id = d.rel_id
WHERE y.update_status = 0
GROUP BY y.rel_id,
d.concept,
d.geo_rfa
Like this, to use the tidiest method.
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT * FROM (SELECT * FROM
(SELECT * FROM (SELECT * FROM
(SELECT * FROM dbo.VBsplit(#mnemonicList,',')) a ) b
JOIN dct ON dct.concept = b.concept WHERE b.geo = dct_variable.geo_rfa) c
JOIN dct_rel z ON c.db_int = z.db_int) d
JOIN rel_d y ON y.rel_id = d.rel_id
WHERE y.update_status = 0
GROUP BY y.rel_id,d.concept,d.geo_rfa
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
However, unless you are using this for reporting purposes on an active database, enabling dirty reads may not be the best way to go.
Edited as (NOLOCK) itself is not deprecated except as described here: http://technet.microsoft.com/en-us/library/ms143729.aspx.
I have a massive query that has been working just fine, however, due to the number or records now in my database, the query is taking longer and longer for the stored procedure to complete.
I had a hard enough time getting the query to work in the first place and I'm not confident in myself to either A) Simplify the query or B) break it into smaller queries/stored procedures.
Can any expert help me out?
SELECT
r.resourceFirstName,
r.resourceLastName,
a.eventDateTime,
CONVERT(char(1), a.eventType) as eventType,
CONVERT(varchar(5), a.reasonCode) as reasonCode,
r.extension,
GETDATE() AS ciscoDate into #temp_Agent
FROM
CCX1.db_cra.dbo.Resource r
INNER JOIN CCX1.db_cra.dbo.AgentStateDetail a
ON r.resourceID = a.agentID
INNER JOIN (
SELECT
p.resourceFirstName,
p.resourceLastName,
MAX(e.eventDateTime) MaxeventDateTime
FROM
CCX1.db_cra.dbo.Resource p
INNER JOIN CCX1.db_cra.dbo.AgentStateDetail e
ON p.resourceID = e.agentID
where
e.eventDateTime > (GETDATE() - 1)
GROUP BY
p.resourceFirstName,
p.resourceLastName
) d
ON r.resourceFirstName = d.resourceFirstName
AND r.resourceLastName = d.resourceLastName
AND a.eventDateTime = d.MaxeventDateTime
AND r.active = 1
where
a.eventDateTime >= (GETDATE() - 7)
ORDER BY
r.resourceLastName,
r.resourceFirstName ASC
Can't give the correct answer having only the query. But...
Consider putting an index on "eventDateTime".
It appears you are joining with a set of records within 1 day. That would make the 7 day filter in the outer query irrelevant. I don't have the ability to test, but maybe your query can be reduced to this? (pseudo code below)
Also consider different solutions. Maybe partition a table based on the datetime. Maybe have a separate database for reporting using star schemas or cube design.
What is being done with the temporary table #temp_Agent?
declare #max datetime = (select max(eventDateTime)
from CCX1.db_cra.dbo.AgentStateDetail
where active=1
and eventDateTime > getdate()-1);
if(#max is null)
exit no records today
SELECT r.resourceFirstName,
r.resourceLastName,
a.eventDateTime,
CONVERT(char(1), a.eventType) as eventType,
CONVERT(varchar(5), a.reasonCode) as reasonCode,
r.extension,
GETDATE() AS ciscoDate
into #temp_Agent
FROM CCX1.db_cra.dbo.Resource r
INNER JOIN CCX1.db_cra.dbo.AgentStateDetail a ON r.resourceID = a.agentID
where r.active = 1
and a.eventDateTime = #max;
Without the full definition of your tables it is hard to troubleshoot why the query is hanging out, but i give you a couple of tips that could help you improve the performance in the Query:
Instead of using a temporary table such as "#temp_Agent" it is preferable to create a local Variable of type "Table". You can achieve exactly the same result but you can drastically increase the performace because:
A local variable of the type "Table" can be created with primary keys and indexes, which improves how SQL finds the information.
A local variable can be clustered, which also improves the performacne in certain scenarios, because the information is accesed directly from disk
A temporary table requires that SQL resolves at runtime the types of columns that should be used to store the information obtained by the query.
If you require to store information in temporary tables, variables, etc, avoid storing unnecesary information in those variables. for example, if you only require latter in your proccess two id colums, then avoid including extra columns that you can retrieve lather
If there is a bunch of information that you need to retireve from multiple sources, you should consider using a view, which also can be indexed and improve the retrieval of the information.
Avoid using unnecesary sorting, grouping, conversions and joins of strings. These particular operations cad degrade drastically the performance of a QUery.
As an extra tip, you can made use of the SQL server tools designed to improve your database and objects:
Check the execution plan of your query (Menu Query->Include Actual Execution Plan, or control + M)
Run the SQL Server Engine Tunning Advisor to analyze a trace file(See SQL Server Profiler) and add extra indexes to improve the database performace
Check with SQL Server Profiler if your Queryes are not generating Deadlocks in the tables you are using to get the information. It is a good practice to use "Hints" in all of your queries to avoid lock problems and other behaviors that in certain scenarios you want to avoid.
Se attached links to better understand what i mean:
Understanding Execution Plan
Usage of Hints
Tunning Options available in SQL Server
I hope the information helps.
Assuming this is SQLServer, try:
WITH CTE AS
(SELECT r.resourceFirstName,
r.resourceLastName,
a.eventDateTime,
CONVERT(char(1), a.eventType) as eventType,
CONVERT(varchar(5), a.reasonCode) as reasonCode,
r.extension,
GETDATE() AS ciscoDate,
RANK() OVER (PARTITION BY r.resourceFirstName, r.resourceLastName
ORDER BY a.eventDateTime DESC) RN
FROM CCX1.db_cra.dbo.Resource r
INNER JOIN CCX1.db_cra.dbo.AgentStateDetail a
ON r.resourceID = a.agentID AND a.eventDateTime >= (GETDATE() - 1)
where r.active = 1)
SELECT resourceFirstName, resourceLastName, eventDateTime, eventType, reasonCode, r.extension, ciscoDate
into #temp_Agent
FROM CTE
WHERE RN=1
ORDER BY r.resourceLastName, r.resourceFirstName ASC