I have a stored procedure that's in development, with multiple UNION ALL statements. This is historical data, and I've been instructed to use SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED. Setting aside the discussion over whether this is the right method, I'd like to know if I need to specify this ISOLATION LEVEL only once at the top of the stored procedure, or if I need to specify it after every UNION ALL, since they're different queries?
Example:
Alter procedure dbo.ExampleProcedure as
declare #StartDate datetime
declare #EndDate datetime
insert into myDB.DBO.InboundCalls
select I.Date, I.System, Count(*) as calls
from
(select Date, System, CallID from System1CallData C
Left Join someothertables as S on C.CallID = S.CallID
where (C.date >= #StartDate and C.date < #EndDate)) as I
Group by I.Date, I.System
Union ALL
select I.Date, I.System, Count(*) as calls
from
(select Date, System, CallID from System2CallData C
Left Join someothertables as S on C.CallID = S.CallID
where (C.date >= #StartDate and C.date < #EndDate)) as I
group by I.Date, I.System
Union ALL
select I.Date, I.System, Count(*) as calls
from
(select Date, System, CallID from System3CallData C
Left Join someothertables as S on C.CallID = S.CallID
where (C.date >= #StartDate and C.date < #EndDate)) as I
Group by I.Date, I.System
Order by I.Date asc, I.System asc, calls asc
So do I put SET TRANSACTION ISOLATION LEVEL after Alter Procedure dbo.ExampleProcedure as, or before the first SELECT, or before each nested SELECT? Thanks in advance for any guidance!
I'd like to know if I need to specify this ISOLATION LEVEL only once at the top of the stored procedure . . .
Only once at the top of the procedure, unless of course you're switching isolation levels within the procedure. The isolation level reverts to the previous level when the SP exits.
If you issue SET TRANSACTION ISOLATION LEVEL in a stored procedure or
trigger, when the object returns control the isolation level is reset
to the level in effect when the object was invoked. For example, if
you set REPEATABLE READ in a batch, and the batch then calls a stored
procedure that sets the isolation level to SERIALIZABLE, the isolation
level setting reverts to REPEATABLE READ when the stored procedure
returns control to the batch.
Using the "read uncommitted" isolation level probably isn't risky on historical data. I'd presume the person instructing you to use that isolation level knows the risks and has determined that it's safe.
Historical data usually either doesn't change at all, or it changes at known intervals. (Say, quarterly. Or daily at 1:00 am.) I'd expect relatively few people to have insert privileges on those tables, and almost nobody to have update and delete privileges.
You might also test running three separate insert statements within a single transaction, rather than inserting the union of three select statements. The ORDER BY clause is probably a bad idea in production.
Related
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
I have a SQL job which kind of process a queue of requests. It updates Table1. It is long process that takes like 15 minutes.
Meanwhile my application tries to read records from Table1 and displays them in a grid. The corresponding get proc has set tran isolation level read uncommited.
When the SQL job is running, my application always time outs while populating the grid. If the SQL job is not running it works fine.
My proc has appropriate isolation level, so I'm not getting why it still time out.
Thoughts?
Here is how my get proc looks like:
CREATE PROCEDURE dbo.MyGetProc(...)
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET NOCOUNT ON;
SELECT
...
FROM
(
SELECT
...
FROM
dbo.Table1
LEFT JOIN dbo.OtherTable1
WHERE ...
GROUP BY
...
) X
INNER JOIN dbo.OtherTable2
LEFT JOIN dbo.OtherTable3
LEFT JOIN dbo.OtherTable4
LEFT JOIN dbo.OtherTable5
LEFT JOIN dbo.OtherTable6
LEFT JOIN dbo.OtherTable6
ORDER BY
...
END
use
Select * from table1 with (nolock)
which reads commited data.
I've been asked for a school exercise to write the following query:
SELECT ACCOUNT_ID, AVG(AMOUNT)
FROM ACCOUNTS A
INNER JOIN TRANSACTIONS T ON A.ACCOUNT_ID = T.ACCOUNT_ID
GROUP BY ACCOUNT_ID
The focus of the exercise is to perform the whole statement without including new transactions registered while it is in progress. My first thought was to use the highest isolation level Serializable or set the transaction Readonly. But I'm wondering however if it's really necessary.
Can be a single statement like this considered atomic? If it's the case, does it mean that the engine works on the datas as they are when the query starts?
If it's not the case, what is the correct way to do that?
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.
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