Deadlock in SQL Server - sql-server

I'm facing deadlock
was deadlocked on lock resources with another process and has been
chosen as the deadlock victim.
problem In SQL-Server as i'm inserting data in database by picking max id against a specific column then add a increment got the value against which record will be inserted.
i'm calling a procedure as code mentioned below:
CREATE
PROCEDURE [dbo].[Web_GetMaxColumnID]
#Col_Name nvarchar(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
DECLARE #MaxID BIGINT;
SET NOCOUNT ON;
-- Insert statements for procedure here
BEGIN
BEGIN TRAN
SET #MaxID = (
SELECT Col_Counter
FROM Maintenance_Counter WITH (XLOCK, ROWLOCK)
WHERE COL_NAME = #Col_Name
)
UPDATE Maintenance_Counter
SET Col_Counter = #MaxID + 1
WHERE COL_NAME = #Col_Name
COMMIT
END
SELECT (
CONVERT(
VARCHAR,
(
SELECT office_id
FROM Maintenance
)
) + '' + CONVERT(VARCHAR, (#MaxID))
) AS MaxID
END
any one help me out .....

As Marc already answered, use SEQUENCE. It's available in all supported versions of SQL Server, ie 2012 and later. The only reason to avoid it is targeting an unsupported version like 2008.
In this case, you can set the counter variable in the same statement you update the counter value. This way, you don't need any transactions or locks, eg:
declare #counterValue bigint
UPDATE Maintenance_Counter
SET Col_Counter = Col_Counter + 1 , #counterValue=Col_Counter+1
WHERE COL_NAME = #Col_Name
select #counterValue

Yo can use sequences to generate incremental values avoiding any blocking.
I have adapted my own Counter Generator to be a direct replacement for yours. It creates dynamically the SQL statements to manage sequences, if a Sequence doesn't exist for the value we are looking for, it creates it.
ALTER PROCEDURE [dbo].[Web_GetMaxColumnID]
#Col_Name nvarchar(50)
AS
declare #Value bigint;
declare #SQL nvarchar(64);
BEGIN
if not exists(select * from sys.objects where object_id = object_id(N'dbo.MY_SEQUENCES_' + #Col_Name) and type = 'SO')
begin
set #SQL = N'create sequence dbo.MY_SEQUENCES_' + #Col_Name + ' as bigint start with 1';
exec (#SQL);
end
set #SQL = N'set #Value = next value for dbo.MY_SEQUENCES_' + #Col_Name;
exec sp_executesql #SQL, N'#Value bigint out', #Value = #Value out;
select #Value ;
END
The only inconvenience is that your values can get gaps within (because you could have retrieved a value but finally not used it). This is not a problem on my tables, but you have to consider it.

Related

TSQL Transaction behaviour with disconnected queries - dynamic SQL

I have the below stored procedure, that dynamically calls a list of stored procedures. It's been in place for a few months and has been running fine (unfortunately my access to the server is pretty restricted so couldn't manage this any other way)
Alter Proc [Process].[UspLoad_LoadController]
(
#HoursBetweenEachRun Int
)
As
Begin
--find all procedures that need to be updated
Create Table [#ProcsToRun]
(
[PID] Int Identity(1 , 1)
, [SchemaName] Varchar(150)
, [ProcName] Varchar(150)
);
Insert [#ProcsToRun]
( [SchemaName]
, [ProcName]
)
Select [s].[name]
, [p].[name]
From [sys].[procedures] [p]
Left Join [sys].[schemas] [s]
On [s].[schema_id] = [p].[schema_id]
Where [s].[name] = 'Process'
And [p].[name] Like 'UspUpdate%';
Declare #MaxProcs Int
, #CurrentProc Int = 1;
Select #MaxProcs = Max([PID])
From [#ProcsToRun];
Declare #SQL Varchar(Max)
, #SchemaName sysname
, #ProcName sysname;
--run through each procedure, not caring if the count changes and only updating if there have been more than 23 hours since the last run
While #CurrentProc <= #MaxProcs
Begin
Select #SchemaName = [SchemaName]
, #ProcName = [ProcName]
From [#ProcsToRun]
Where [PID] = #CurrentProc;
Select #SQL = #SchemaName + '.' + #ProcName
+ ' #PrevCheck = 0,#HoursBetweenUpdates = '
+ Cast(#HoursBetweenEachRun As Varchar(5));
Exec (#SQL);
Set #CurrentProc = #CurrentProc + 1;
End;
End;
Go
However, the environment this is running in occasionally suffers from communications errors, with the query being cancelled whilst it is still executing.
My question is - can I wrap the entire procedure with a transaction statement and if I can what would happen in the event of the query being terminated early?
BEGIN Tran Test
Exec [Process].[UspLoad_LoadController] #HoursBetweenEachRun = 1;
COMMIT TRANSACTION Test
What I want to happen would be for the transaction to be rolled back - would this cater for this?
Yes it works,but you might have to see how many stored procs you have and impact of rollback.Normally you can use Set XACT_ABORT ON inside stored proc,but due to dynamic SQL,it wont have any effect..
Sample demo on how to wrap your proc
begin try
begin tran
exec usp_main
commit
end try
begin catch
rollback
end catch
some tests i did on trying to use XACT_ABORT with out any success.but wrapping your main proc in a tran and rolling back when any error occurs,rollback all stored procs too.
create table test2
(
id int)
create table test3
(
id int)
create proc usp_test2
as
begin
insert into test2
select 1
end
alter proc usp_test3
as
begin
insert into test3
select 1/0
end
alter proc usp_main
as
begin
set xact_abort on
declare #sql1 nvarchar(2000)
set #sql1='exec usp_test2'
declare #sql2 nvarchar(2000)
set #sql2='exec usp_test3'
exec (#sql1)
exec(#sql2)
end

SQL SERVER : How to execute an SP specifed number of time without using loop

I have a scenario wherein i have to execute an SP for specified number of time(number of execution will be mentioned by user) without using loop.
My SP is setting an OUTPUT variable of varchar type. I am willing to insert the output of my SP into a temp table and use it for further processing.
I am unable to modify this SP into function as it contain an Update statement.
Kindly suggest if we can do so without loop.
Whit this solution you do not need an output; #res is your result directly set in a temp table.
CREATE PROCEDURE [dbo].[myStoredProc]
#Counter int,
#params nvarchar(64),
#CreateTable bit
AS
DECLARE #res varchar(64)
IF #CreateTable = 1
BEGIN
IF EXISTS (SELECT 1 FROM dbo.sysobjects WHERE id = object_id(N'[#tempTable]'))
DROP TABLE #tempTable
CREATE TABLE #tempTable ([Res] [nvarchar] (64))
END
SET #res = CONVERT(varchar(64), #Counter)
SET #Counter = #Counter - 1
IF #Counter > 0
exec myStoredProc #Counter, #params, 0
INSERT #tempTable VALUES (#res)
IF #CreateTable = 1
BEGIN
SELECT * FROM #tempTable
DROP TABLE #tempTable
END
GO
DECLARE #o varchar(64)
exec [myStoredProc] 5, '', 1

How can I loop through SQL tables?

I'm not convinced my title accurately represents my problem, but here is what I have:
DECLARE #ListOfTables TABLE(
RowID INT NOT NULL PRIMARY KEY IDENTITY(1,1)
, TableName VARCHAR(200)
)
DECLARE #RowsToProcess INT
DECLARE #CurrentRow INT
DECLARE #TableName VARCHAR(200)
INSERT INTO #ListOfTables (TableName)
VALUES ('Books')
INSERT INTO #ListOfTables (TableName)
VALUES ('Magazines')
INSERT INTO #ListOfTables (TableName)
VALUES ('Essays')
SET #RowsToProcess = ##ROWCOUNT
SET #CurrentRow = 0
I want to then loop through this table and delete.
WHILE #CurrentRow < #RowsToProcess
BEGIN
SET #CurrentRow = #CurrentRow+1
SELECT #TableName = TableName
FROM #ListOfTables
WHERE RowID = #CurrentRow
DELETE FROM #TableName
END
In actuality, my delete isn't just deleting the table - it has specific criteria. I've simplified it for demonstration purposes.
The error I get here is: Must declare the variable '#TableName'.
I've moved it into the WHILE loop and I receive the same error.
What can I do here? Is it possible to do what I'm trying to do?
Assuming you actually have tables in your db named books, magazines, and essays, and you want to delete from them, or do some query on them, you need to use dynamic sql. Not sure how it's done in MySQL, but in SQL-Server, it'd be like this (highly simplified example):
SET #sql = 'DELETE FROM ' + #TableName;
EXEC (#sql);

Querying LDAP from SQL Server - Best Practice

I'm wondering if the current process I'm using to update a table of user's (tblUsers) Windows ID's (NTID) is a good method. I'm wondering because LDAP will only return 1000 rows I believe, so that prevents me from just doing it all in one query.
tlbUsers has about 160,000 rows. I'm querying LDAP to update the NTID of each record in tblUsers. I'm using a linked server to ADSI to view LDAP data. My process uses two stored procedures, one for getting a WindowsID from LDAP (LdapPackage.GetUserNTID), another for updating the rows in tblUsers (LdapPackage.UpdateUserNTID).
The code below works for updating the table, however, it's pretty slow. It would seem to me this isn't the best way of doing it, that if I wanted to do a batch update like this from LDAP, there should be a simpler way than updating a record at a time.
This previous post gave an interesting example using UNION's to get around the 1000 record limit, but it only works if each query returns less than 1000 records, which at a large company would probably require lots of UNIONS... at least that's my initial take on it.
Querying Active Directory from SQL Server 2005
Thanks in advance guys!!!
<code>
CREATE PROCEDURE LdapPackage.GetUserNTID
(
#EmployeeID INT,
#OutNTID VARCHAR(20) OUTPUT
)
AS
BEGIN
DECLARE #SQLString NVARCHAR(MAX)
DECLARE #ParmDefinition NVARCHAR(MAX)
DECLARE #LdapFilter NVARCHAR(100)
--DECLARE #NTID VARCHAR(20)
SET #LdapFilter = 'employeeNumber = ' + CAST(#EmployeeID AS NVARCHAR(20))
SET #SQLString = 'SELECT DISTINCT #pNTID = samAccountName
FROM OPENQUERY(LDAP,
''select samAccountName, Mail
from ''''GC://domain.company.com''''
where objectClass=''''user'''' AND objectCategory=''''person'''' and ' + #LdapFilter + ''')
WHERE Mail IS NOT NULL'
SET #ParmDefinition = N'#pNTID varchar(20) OUTPUT'
EXECUTE sp_executesql
#SQLString,
#ParmDefinition,
#pNTID=#OutNTID OUTPUT
--SELECT NTID = #OutNTID
END
</code>
<code>
CREATE PROCEDURE LdapPackage.UpdateUserNTID
AS
BEGIN
DECLARE #EmployeeID AS INT
DECLARE #NTID AS VARCHAR(20)
DECLARE #RowCount AS INT
DECLARE #SQLString AS NVARCHAR(MAX)
DECLARE #ParmDefinition AS NVARCHAR(200)
SET #RowCount = 1
DECLARE Persons CURSOR
FOR SELECT DISTINCT EmployeeID FROM tblUsers
OPEN Persons
FETCH NEXT FROM Persons INTO #EmployeeID
WHILE ##FETCH_STATUS = 0
BEGIN
--GET NTID
SET #SQLString =N'EXEC LdapPackage.GetUserNTID #pEmployeeID, #pNTID OUTPUT'
SET #ParmDefinition =N'#pEmployeeID INT, #pNTID VARCHAR(20) OUTPUT'
EXECUTE sp_executesql
#SQLString,
#ParmDefinition,
#pEmployeeID=#EmployeeID,
#pNTID=#NTID OUTPUT
--UPDATE NTID
/*PRINT 'RowCount = ' + CAST(#RowCount AS VARCHAR(10))
PRINT 'EmployeeID = ' + CAST(#EmployeeID AS VARCHAR(20))
PRINT 'NTID = ' + #NTID
PRINT '-----------------------------'*/
UPDATE tblUsers
SET NTID = #NTID
WHERE EmployeeID = #EmployeeID
SET #RowCount = #RowCount + 1
FETCH NEXT FROM Persons INTO #EmployeeID
END
CLOSE Persons
DEALLOCATE Persons
END
</code>
my solution here was to have my that linked servers record limit to LDAP increased by the system admin. I would have preferred to have identified some sort of SQL Server interface like Oracle appears to have... so maybe I'll get to that in the future.

How to write Batch SQL query in procedure

if i want to write a procedure like below, is there some other way that,
to avoid using concatenate SQL statement, i am just afraid, if the input is too long, exceed the limit of max varchar, the code will have big problem.
Thanks
CREATE PROCEDURE UPDATE_ALL_STATUS
#IDs varchar(MAX) = null,
#status int = null
AS
BEGIN
IF #IDs is null
BEGIN
RETURN
END
DECLARE #SQL VARCHAR(MAX)
SET #SQL = 'UPDATE mytable SET status = ' + #status + ' WHERE id in (' + #IDs + ')'
EXECUTE #SQL
END
Instead of dynamic SQL (which is also vulnerable to SQL Injection Attacks) and passing in a VARCHAR(MAX), consider using Table Valued Parameters:
-- Creates the TVP type - only needed once!
CREATE TYPE IntegerTableType AS TABLE
( Identities INT );
GO
CREATE PROCEDURE UPDATE_ALL_STATUS
#IDs IntegerTableType READONLY,
#status int = null
AS
BEGIN
UPDATE mytable
SET status = #status
WHERE id IN
(SELECT Identities FROM #IDs)
END
This MSDN article shows how to call these from your .NET code.

Resources