SQL Locking - Timeout issue - locking

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.

Related

How does SQL Server add the with (nolock) one time and effect all query?

If I use N tables now, need to add N times with (nolock), for example
select * from T1 with (nolock)
join T2 with (nolock) ..
join T3 with (nolock) ..
join T4 with (nolock) ..
....
If I want to declare it once, what can I make the next all query work with (nolock)
like :
use all with (nolock)
select * from T1
join T2 ..
join T3 ..
join T4 ..
....
You can use set Transaction isolation level read uncommitted, e.g:
set Transaction isolation level read uncommitted
select * from T1
join T2 ..
join T3 ..
join T4 ..
Notice:
Transactions running at the READ UNCOMMITTED level do not issue shared locks to prevent other transactions from modifying data read by the current transaction. READ UNCOMMITTED transactions are also not blocked by exclusive locks that would prevent the current transaction from reading rows that have been modified but not committed by other transactions. When this option is set, it is possible to read uncommitted modifications, which are called dirty reads. Values in the data can be changed and rows can appear or disappear in the data set before the end of the transaction.
from SET TRANSACTION ISOLATION LEVEL (Transact-SQL) - SQL Server | Microsoft Docs

Is it possible to pick one result set to return from a batch execution?

I have to retrieve some data from a MSSQL 2014 server through a propriatery application which then uses an odbc data source. The only thing I can modify is the query the application uses. I cannot modify the application or how the application handles the results.
The following query is doing what I want if I execute it directly e.g. in Heidi.
USE MY_DB;
BEGIN TRANSACTION
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
DECLARE #myvar1 INT = 2;
DECLARE #myvar2 INT = 2;
PRINT #myvar1;
SELECT TOP 20 [vwPGIA].[OrNum],[vwPGIA].[DBM],[vwPGIA].[MBM],[vwPGIA].[MN],[NOMID],[Priority],SUBSTRING([Comment],0,254) AS Comment,[TLSAP],[Box],[SequenceNumber]
INTO #tmp_tbl
FROM [MY_DB].[dbo].[vwPGIA]
INNER JOIN [MY_DB].[dbo].[tblDLA] ON [dbo].[tblDLA].[OrNum]=[dbo].[vwPGIA].[OrNum]
INNER JOIN [dbo].[tblMDM] ON [vwPGIA].[MBM]=[tblMDM].[MBM]
WHERE ([TLSAP] = #myvar1)
AND [vwPGIA].[MBM] NOT IN (SELECT [MBM] FROM [MY_DB].[dbo].[vwDPS])
AND [vwPGIA].[OrNum] NOT IN (SELECT [OrNum] FROM [MY_DB].[dbo].[vwDPS] WHERE [MY_DB].[dbo].[vwDPS].[TLR] <> #myvar1)
ORDER BY [SequenceNumber];
SELECT TOP 1 [OrNum],[DBM],[MBM],[MN],[NOMID],[Priority],[Comment],[TLSAP],[Box],[WTT],[SequenceNumber]
FROM #tmp_tbl
INNER JOIN [dbo].[tblTBN] ON [Box]=[BoxN]
WHERE ([WTT]=#myvar2)
ORDER BY [SequenceNumber];
INSERT INTO [dbo].[tblDPS]
(OrNum,DBM,MBM,State,StateStartTime,Info,TLR)
SELECT TOP 1 [OrNum],[DBM],[MBM],'1',GETDATE(),'info',#myvar1
FROM #tmp_tbl
INNER JOIN [dbo].[tblTBN] ON [Box]=[BoxN]
WHERE ([WTT]=#myvar2)
ORDER BY [SequenceNumber]
;
DROP TABLE #tmp_tbl;
COMMIT TRANSACTION
Running this through the ODBC interface results in an empty result. The problem seems to be, that I am doing a batch request which results in multiple result sets. The application probably only handles the first result set or maybe cannot handle more than one result set.
Finally the question: Is there a way or workaround to reduce the result sets to only the one returned by the SELECT TOP 1 ... part?

How to find out why the status of a spid is suspended? What resources the spid is waiting for?

I run EXEC sp_who2 78 and I get the following results:
How can I find why its status is suspended?
This process is a heavy INSERT based on an expensive query. A big SELECT that gets data from several tables and write some 3-4 millions rows to a different table.
There are no locks/ blocks.
The waittype it is linked to is CXPACKET. which I can understand because there are 9 78s as you can see on the picture below.
What concerns me and what I really would like to know is why the number 1 of the SPID 78 is suspended.
I understand that when the status of a SPID is suspended it means the process is waiting on a resource and it will resume when it gets its resource.
How can I find more details about this? what resource? why is it not available?
I use a lot the code below, and variations therefrom, but is there anything else I can do to find out why the SPID is suspended?
select *
from sys.dm_exec_requests r
join sys.dm_os_tasks t on r.session_id = t.session_id
where r.session_id = 78
I already used sp_whoisactive. The result I get for this particular spid78 is as follow: (broken into 3 pics to fit screen)
SUSPENDED:
It means that the request currently is not active because it is waiting on a resource. The resource can be an I/O for reading a page, A WAITit can be communication on the network, or it is waiting for lock or a latch. It will become active once the task it is waiting for is completed. For example, if the query the has posted a I/O request to read data of a complete table tblStudents then this task will be suspended till the I/O is complete. Once I/O is completed (Data for table tblStudents is available in the memory), query will move into RUNNABLE queue.
So if it is waiting, check the wait_type column to understand what it is waiting for and troubleshoot based on the wait_time.
I have developed the following procedure that helps me with this, it includes the WAIT_TYPE.
use master
go
CREATE PROCEDURE [dbo].[sp_radhe]
AS
BEGIN
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT es.session_id AS session_id
,COALESCE(es.original_login_name, '') AS login_name
,COALESCE(es.host_name,'') AS hostname
,COALESCE(es.last_request_end_time,es.last_request_start_time) AS last_batch
,es.status
,COALESCE(er.blocking_session_id,0) AS blocked_by
,COALESCE(er.wait_type,'MISCELLANEOUS') AS waittype
,COALESCE(er.wait_time,0) AS waittime
,COALESCE(er.last_wait_type,'MISCELLANEOUS') AS lastwaittype
,COALESCE(er.wait_resource,'') AS waitresource
,coalesce(db_name(er.database_id),'No Info') as dbid
,COALESCE(er.command,'AWAITING COMMAND') AS cmd
,sql_text=st.text
,transaction_isolation =
CASE es.transaction_isolation_level
WHEN 0 THEN 'Unspecified'
WHEN 1 THEN 'Read Uncommitted'
WHEN 2 THEN 'Read Committed'
WHEN 3 THEN 'Repeatable'
WHEN 4 THEN 'Serializable'
WHEN 5 THEN 'Snapshot'
END
,COALESCE(es.cpu_time,0)
+ COALESCE(er.cpu_time,0) AS cpu
,COALESCE(es.reads,0)
+ COALESCE(es.writes,0)
+ COALESCE(er.reads,0)
+ COALESCE(er.writes,0) AS physical_io
,COALESCE(er.open_transaction_count,-1) AS open_tran
,COALESCE(es.program_name,'') AS program_name
,es.login_time
FROM sys.dm_exec_sessions es
LEFT OUTER JOIN sys.dm_exec_connections ec ON es.session_id = ec.session_id
LEFT OUTER JOIN sys.dm_exec_requests er ON es.session_id = er.session_id
LEFT OUTER JOIN sys.server_principals sp ON es.security_id = sp.sid
LEFT OUTER JOIN sys.dm_os_tasks ota ON es.session_id = ota.session_id
LEFT OUTER JOIN sys.dm_os_threads oth ON ota.worker_address = oth.worker_address
CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) AS st
where es.is_user_process = 1
and es.session_id <> ##spid
ORDER BY es.session_id
end
This query below also can show basic information to assist when the spid is suspended, by showing which resource the spid is waiting for.
SELECT wt.session_id,
ot.task_state,
wt.wait_type,
wt.wait_duration_ms,
wt.blocking_session_id,
wt.resource_description,
es.[host_name],
es.[program_name]
FROM sys.dm_os_waiting_tasks wt
INNER JOIN sys.dm_os_tasks ot ON ot.task_address = wt.waiting_task_address
INNER JOIN sys.dm_exec_sessions es ON es.session_id = wt.session_id
WHERE es.is_user_process = 1
Please see the picture below as an example:
I use sp_whoIsActive to look at this kind of information as it is a ready made free tool that gives you good information for troubleshooting slow queries:
How to Use sp_WhoIsActive to Find Slow SQL Server Queries
With this, you can get the query text, the plan it is using, the resource the query is waiting on, what is blocking it, what locks it is taking out and a whole lot more.
Much easier than trying to roll your own.
You can solve it with to ways:
Fix the cluster index.
Use temporal tables to get a part of the all table and work with it.
I have the same problem with a table with a 400,000,000 rows, and use a temporal tables to get a part of it and then i use my filters and inners because change the index was not a option.
Some example:
--
--this is need be cause DECLARE #TEMPORAL are not well for a lot of data.
CREATE TABLE #TEMPORAL
(
ID BIGINT,
ID2 BIGINT,
DATA1 DECIMAL,
DATA2 DECIMAL
);
WITH TABLE1 AS
(
SELECT
L.ID,
L.ID2,
L.DATA
FROM LARGEDATA L
WHERE L.ID = 1
), WITH TABLE2 AS
(
SELECT
L.ID,
L.ID2,
L.DATA
FROM LARGEDATA L
WHERE L.ID = 2
) INSERT INTO #TEMPORAL SELECT
T1.ID,
T2.ID,
T1.DATA,
T2.DATA
FROM TABLE1 T1
INNER JOIN TABLE2 T2
ON T2.ID2 = T2.ID2;
--
--this take a lot of resources proces and time and be come a status suspend, this why i need a temporal table.
SELECT
*
FROM #TEMPORAL T
WHERE T.DATA1 < T.DATA2
--
--IMPORTANT DROP THE TABLE.
DROP TABLE #TEMPORAL

long running sp

The following sp:
I have a stored procedure which runs anywhere from 1/2 minute to 4 hours (during nightly processing):
update tableA
set tableA.Other_Flag_50 = isnull(Staging.other_flag_50, 0)
from tableA
inner join (
select acct_nbr,
appl_code,
Other_Flag_50
from tableB
) Staging on tableA.lnhist_acct_nbr = Staging.acct_nbr
and tableA.lnhist_appl_code = Staging.appl_code
I ran Blocking reports in Profiler for 2 nights in a row, first at 10 minutes interval then at 5 minutes. The stored procedure never shows up as being blocked (but it blocks other queries).
Any ideas on optimizing this? Would creating a view with the join help? (acct_nbr, appl_code, Other_Flag_50 from tableB) Thanks!!
Have you tried doing the INNER JOIN directly to tableB?
UPDATE tableA
SET tableA.Other_Flag_50=isnull(tableB.other_flag_50,0)
FROM tableA
INNER JOIN tableB
ON tableA.lnhist_acct_nbr = tableB.acct_nbr
AND tableA.lnhist_appl_code = tableB.appl_code
Try using rowlock to prevent locking a whole table.
update tableA with (rowlock)
...
EDIT
Not sure about other RDBMS but the answer I provided works for SQL Server

SQL nolock and join

I am using a process that inserts data in 2 tables with rowlock, continuously. In the same time I want to use some queries on these tables. As I said the inserts are done with (rowlock) and I use for the queries the isolation level read uncomitted and nolock.
When I use the queries on a single table they work perfectly, but when I try to join the 2 tables I get this error:
Transaction (Process ID 88) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
Meanwhile, if I use the sp_lock procedure I found that the Key lock becomes a tab lock when I perform my queries.
Does anyone know if there is a special relation between (nolock) and join? And if there is how can I avoid it.
UPDATE:
Insert into tbl1 with (rowlock)
(
col1,
col2,
col3
)
select * from #tbl_temp
( this is in an infinite loop and the data from #tbl_temp is always changed. Actualy this is a more complex process but this is the idea.)
Insert into tbl2 with (rowlock)
(
col3,
col4,
col5
)
select * from #tbl_temp2
In the same time I perform
set transaction isolation level read uncomitted
select col1,col2,col3
from tbl1 with (nolock) -- works fine
select col1,col2,a.col3
from tbl1 with (nolock) join tbl2 with (nolock) on (tbl1.col3 = tbl2.col3)
-- deadlock
You might want to try turning on READ_COMMITTED_SHAPSHOT isolation level for your database.
(But be aware that this will put increased pressure on tempDB)