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.
CREATE PROCEDURE [dbo].[sp_HeatMap_Paper]
#Grade varchar(150)=NULL,
#Site varchar(250)=NULL,
#TRef varchar(15)=NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #uregref varchar(50), #regTID varchar(8),
#testValue varchar(80), #testResultID int,
#lowerL1 varchar(20), #upperL1 varchar(20),
#lowerL2 varchar(20), #upperL2 varchar(20)
BEGIN TRANSACTION
BEGIN TRY
DELETE FROM HeatMap;
select top 1 #uregref = URegRef from NA_PAPER_HEAT_MAP where RSDESCRIPTION= #Grade and BOX_PLANT1= #Site;
select #regTID = RegTID from REGKEY where URegRef = #uregref;
select #testValue=TestResult,#testResultID=Result_ID from RESULTDATA where RegTID=#regTID and TRef=#TRef;
SELECT #lowerL1=Lower, #upperL1=Upper from ResultLimit WHERE Priority = 1 and Result_Id=#testResultID;
SELECT #lowerL2=Lower, #upperL2=Upper from ResultLimit WHERE Priority = 2 and Result_Id=#testResultID;
Insert into HeatMap (Grade,Site,TestValue,TRef,LowerLimitL1,UpperLimitL1,LowerLimitL2,UpperLimitL2)
values (#Grade,#Site,#testValue,#TRef,#lowerL1,#upperL1,#lowerL2,#upperL2)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
Return Error_Message()
END CATCH
END
GO
I want to pass a view name into this stored procedure, here 'NA_PAPER_HEAT_MAP' is the view instead of this I want to pass a parameter #viewName
You can build dynamic SQL and execute it using sys.sp_executesql to execute it.
I'll give you an example for how to use it.
CREATE PROCEDURE usp_selectView
#id INT,
#viewName NVARCHAR(1000)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX), #paramDef NVARCHAR(MAX);
-- build dynamic SQL
-- you can build whatever SQL you want. This is just an example
-- make sure you sanitize #viewName to avoid SQL injection attack
SET #sql = 'SELECT * FROM ' + #viewName + ' WHERE Id = #selectedId';
-- dynamic SQL parameter definition
SET #paramDef = '#selectedId INT';
-- here, execute the dynamic SQL
EXEC sys.sp_executesql #sql, #paramDef, #selectedId = #id
END
Subject:
I've got a report that I create every month. The creation of report consists of 2 steps:
Get an XML from our service and store it in DB;
Parse XML and create file.
For the last few month I've created report in manual mode. And now I want to automate this stuff.
But here comes a
Problem:
The second step (parsing XML and file creation) runs like a charm, but with first step I'm observing weird behaviour.
I got Stored Procedure which gets XML:
ALTER PROCEDURE [Structure].[GetXML]
#LastActDate date,
#CurActDate date
AS
BEGIN
SET NOCOUNT ON;
begining:
DECLARE #URI varchar(2000),
#methodName varchar(50),
#objectID int,
#hResult int,
#setTimeouts nvarchar(255),
#serv nvarchar(255) = 'http://example.com/docs/',
#result nvarchar(max) = ''
DECLARE #t TABLE(Resp nvarchar(max))
declare #timeStamp nvarchar(50) = convert(nvarchar(50),CURRENT_TIMESTAMP,127)
declare #CurDate date = dateadd(day,0,getdate())
--EXEC #hResult = sp_OACreate 'WinHttp.WinHttpRequest.5.1', #objectID OUT
EXEC #hResult = sp_OACreate 'MSXML2.XMLHTTP', #ObjectID OUT
SELECT #URI = #serv + '.newchange?ds='+CONVERT(nvarchar(10),#LastActDate,104)+'&df='+CONVERT(nvarchar(10),#CurActDate,104)+'&pardaily=1',
#methodName='GET',
#setTimeouts = 'setTimeouts(9000,90000,900000,9000000)'
EXEC #hResult = sp_OAMethod #objectID, 'open', null, #methodName, #URI, 'false'
EXEC #hResult = sp_OAMethod #objectID, #setTimeouts
EXEC #hResult = sp_OAMethod #objectID, 'send', null
INSERT INTO #t
EXEC sp_OAGetProperty #objectID, 'responseText'
SELECT top 1 #result = Resp
FROM #t
if #result is null
begin
delete from #t
exec sp_OAGetErrorInfo #objectID
exec sp_OADestroy #objectID
goto begining
end
else
begin
INSERT INTO Structure.MonthlyRow
SELECT #timeStamp, #result
end
END
When I run this SP like
EXEC [Structure].[GetXML] '2016-06-01', '2016-07-01'
I got a row in Structure.MonthlyRow table with correct timestamp and response (the average length is ~70k symbols)
Here is creation script of a table:
CREATE TABLE Structure.MonthlyRow(
[timestamp] nvarchar(50) NOT NULL,
[RowResp] nvarchar(max) NULL,
CONSTRAINT [PK_dDayly] PRIMARY KEY CLUSTERED ([timestamp] DESC))
If I create a job that launch this SP I get a row in table with results, and the length of result is 512 symbols! It is a proper part of XML that looks like it was truncated from nvarchar(max) to nvarchar(512), but I have no variables or table columns with length of 512 that are used.
What have I tried:
Run as user with my account in Job Step properties;
Job was started by schedule or manually;
Add WITH EXECUTE AS OWNER in SP;
Tried using WinHttp.WinHttpRequest.5.1 and MSXML2.XMLHTTP.
Question:
What possibly could be a problem? Why I am getting correct results when I run my SP manually, and got only 512 symbols of response when run SP as job step?
Note:
Yes, I know that getting XML from web-service is better handled by PHP, C# or even PowerShell and if I can not find a solution I will use one of them.
add this line at the top of your sp or in the job before EXEC of your sp
SET TEXTSIZE 2147483647;
the problem is that jobs set a default
SET TEXTSIZE 1024
this limits data returned to 1024 chars (512 for nchars)
Part 1
DECLARE #A INT
DECLARE #B NVARCHAR(20)
SET #A=123
SET #B='#A'
Part 2
DECLARE #SQL NVARCHAR(MAX)
SET #SQL='SELECT ' + #B
EXEC SP_EXECUTESQL #SQL
--Should return 123
Directly referencing #A in non-dynamic SQL would not be acceptable for this task.
The above in Part 2 is generically what I am trying to do. I understand the variable is out of scope and it won't work as done above. How could I use #B to get the value of #A?
UPDATE 20190322:
I actually forgot about this question and implemented logging on the C# side instead of the database, but I got curious again if this was possible. Again, this needs to be generic as I would want to put it into the tops of any stored procedure and I do not want to customize it per sproc; I'm having trouble in the cursor getting the value of a parameter. Here is a working example:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[LoggingTest]
#DateBegin datetime,
#DateEnd datetime,
#Person varchar(8000),
#Unit varchar(8000)
AS
BEGIN
--BEGIN LOGGING CODE
DECLARE #String NVARCHAR(MAX)=''
DECLARE #Parameter_name nvarchar(2000), #type nvarchar(50), #length SMALLINT, #Prec SMALLINT, #Scale SMALLINT, #Param_order SMALLINT, #Collation nvarchar(2000);
DECLARE param_cursor CURSOR FOR
SELECT
'Parameter_name' = name,
'Type' = type_name(user_type_id),
'Length' = max_length,
'Prec' = case when type_name(system_type_id) = 'uniqueidentifier'
then precision
else OdbcPrec(system_type_id, max_length, precision) end,
'Scale' = OdbcScale(system_type_id, scale),
'Param_order' = parameter_id,
'Collation' = convert(sysname,
case when system_type_id in (35, 99, 167, 175, 231, 239)
then ServerProperty('collation') end)
from sys.parameters
where object_id = object_id(OBJECT_NAME(##PROCID))
OPEN param_cursor
FETCH NEXT FROM param_cursor
INTO #Parameter_name,#type,#length,#Prec,#Scale,#Param_order,#Collation
WHILE ##FETCH_STATUS = 0
BEGIN
SET #String=#String + #Parameter_name + '==' --+ SELECT #Parameter_name --This is part I can't think of a way to do; need to record/capture the value
SET #String=#String + CHAR(13) + CHAR(10)
FETCH NEXT FROM param_cursor
INTO #Parameter_name, #type,#length,#Prec,#Scale,#Param_order,#Collation
END
CLOSE param_cursor;
DEALLOCATE param_cursor;
--INSERT DATA INTO LOG TABLE HERE
SELECT OBJECT_SCHEMA_NAME(##PROCID) + '.' + OBJECT_NAME(##PROCID) AS [ProcedureName],#String AS [Data],GETDATE() AS [LogTime]
--END LOGGING CODE
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
DO BUSINESS STUFF HERE!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
/*
BLAH
BLAH
BLAH
DECLARE #Now DATETIME=GETDATE()
EXEC [dbo].[LoggingTest] #Now,#Now,'Person Value','Unit Value'
*/
END
GO
I didn't understand the whole thing you are asking, but you can define the variables on sp_executesql:
EXEC SP_EXECUTESQL #SQL, N'#A INT', #A = #A
After searching around, I found that one can
Have the running procedure code from dm_exec_requests & dm_exec_sql_text
Have the procedure call from dm_exec_input_buffer. I should note that I am not very proficient in this, and I think I read somewhere it does not work on queries done outside SSMS....
As already seen in your code, the parameters can be found in sys.parameters
Thus, I implemented a way to do this. Main algorithm steps are:
Create a dummy proc with same params and default values. Change its body to a single SELECT. By using a dynamic xml-concatenation of the parameters at the original proc which is one level higher, we can provide the dummy proc a VALUES table which includes both the parameter names (in quotes) and their values(no quotes).
Exec the dummy proc by using the same call parameters used in the original spc. INSERT the EXEC result-set into a temp table. Drop the dummy proc
afterwards.
Now we have a temp table with both parameter names AND values.
Most of the new code is before yours starts, except from the actual value you need in the cursor. Also, to avoid furious parsing, I used a (cheap) trick of adding a comment --postBeginParserMarker just after the BEGIN so that I know where the proc begins...
alter PROCEDURE [dbo].[LoggingTest]
#DateBegin datetime,
#DateEnd datetime,
#Person varchar(8000),
#Unit varchar(8000)='someunithere'
AS
BEGIN --postBeginParserMarker
declare #SPID int= ##SPID;
declare #request_id int;
declare #dummyspc nvarchar(max)
select #dummyspc = 'spc_dummy_'+ convert(nvarchar,max(object_id)+1) from sys.procedures -- just a way to ensure no conflicts between simultaneous runs of this very proc
--select #dummyspc
declare #thisprocname sysname
select #thisprocname=o.name,#request_id=request_id
from sys.dm_exec_requests r
cross apply sys.dm_exec_sql_text(r.sql_handle) t
inner join sys.objects o on t.objectid=o.object_id
where r.session_id=#SPID
--select #thisprocname
declare #newproc nvarchar(max)
SELECT #newproc=substring(st.text,1,CHARINDEX ('--postBeginParserMarker' , st.text)-1 )
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) st
where r.session_id=#SPID
set #newproc=replace(#newproc,#thisprocname,#dummyspc)
SELECT #newproc+=
char(13)+char(10)
+'select * from ( values'
+STUFF
(
(
SELECT char(13)+char(10)+char(9)+',('+convert(nvarchar,parameter_id)+','''+name+''',convert(nvarchar,'+name+'))'
from sys.parameters
where object_id=object_id(#thisprocname)
FOR XML PATH('') , TYPE
).value('.','nvarchar(max)')
, 4, 1, ' '
)
+char(13)+char(10)+')t(parameter_id,parameter_name,parameter_value)'
+char(13)+char(10)+'END'
--select #newproc
declare #newproccall nvarchar(max)
select #newproccall=event_info from sys.dm_exec_input_buffer ( #SPID ,#request_id)
set #newproccall=replace(#newproccall,#thisprocname,#dummyspc)
--select #newproccall
exec(#newproc)
if object_id('tempdb..#paramtbl') is not null drop table #paramtbl
create table #paramtbl (parameter_id int,parameter_name nvarchar(max),parameter_value nvarchar(max))
insert #paramtbl(parameter_id,parameter_name,parameter_value)
exec(#newproccall)
-- select * from #paramtbl <-- Now this has all you need
exec('drop procedure '+#dummyspc)
--BEGIN LOGGING CODE
DECLARE #String NVARCHAR(MAX)=''
DECLARE #Parameter_name nvarchar(2000), #type nvarchar(50), #length SMALLINT, #Prec SMALLINT, #Scale SMALLINT, #Param_order SMALLINT, #Collation nvarchar(2000);
--select #Unit as val
--drop table ##a
--select ##SPID
DECLARE param_cursor CURSOR FOR
SELECT
'Parameter_name' = name,
'Type' = type_name(user_type_id),
'Length' = max_length,
'Prec' = case when type_name(system_type_id) = 'uniqueidentifier'
then precision
else OdbcPrec(system_type_id, max_length, precision) end,
'Scale' = OdbcScale(system_type_id, scale),
'Param_order' = parameter_id,
'Collation' = convert(sysname,
case when system_type_id in (35, 99, 167, 175, 231, 239)
then ServerProperty('collation') end)
from sys.parameters
where object_id = object_id('LoggingTest')
OPEN param_cursor
FETCH NEXT FROM param_cursor
INTO #Parameter_name,#type,#length,#Prec,#Scale,#Param_order,#Collation
WHILE ##FETCH_STATUS = 0
BEGIN
SET #String=#String + #Parameter_name + '==' + (SELECT isnull(parameter_value,'<NULL>') from #paramtbl where parameter_id=#Param_order)
SET #String=#String + CHAR(13) + CHAR(10)
FETCH NEXT FROM param_cursor
INTO #Parameter_name, #type,#length,#Prec,#Scale,#Param_order,#Collation
END
CLOSE param_cursor;
DEALLOCATE param_cursor;
--INSERT DATA INTO LOG TABLE HERE
SELECT OBJECT_SCHEMA_NAME(##PROCID) + '.' + OBJECT_NAME(##PROCID) AS [ProcedureName],#String AS [Data],GETDATE() AS [LogTime]
--END LOGGING CODE
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
DO BUSINESS STUFF HERE!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
/*
BLAH
BLAH
BLAH
DECLARE #Now DATETIME=GETDATE()
EXEC [dbo].[LoggingTest] #Now,#Now,'Person Value','Unit Value'
*/
END
GO
Though you don't have to do this, but still here is the way:
SET #B=#A
And you can directly assign value of #A:
DECLARE #SQL NVARCHAR(MAX)
SET #SQL='SELECT ' + Convert(varchar(50),#A)
Starting from SQL Server 2014 SP2 there is a sys.dm_exec_input_buffer dynamic management view.
In previous versions there is DBCC INPUTBUFFER.
It returns a table, which contains a column event_info, which contains "The text of the statement in the input buffer for the given spid."
So, you can add a simple query to the start of your stored procedure, something like this:
INSERT INTO LoggingTable(event_info, event_type, parameters)
SELECT
InBuf.event_info
,InBuf.event_type
,InBuf.parameters
FROM
sys.dm_exec_requests AS Req
INNER JOIN sys.dm_exec_sessions AS Ses ON Ses.session_id = Req.session_id
CROSS APPLY sys.dm_exec_input_buffer(Req.session_id, Req.request_id) AS InBuf
WHERE
Ses.session_id > 50
AND Ses.is_user_process = 1
;
If you call/run your stored procedure using EXEC, such as
EXEC [dbo].[LoggingTest]
#DateBegin = '2019-01-01',
#DateEnd = '2020-01-01',
#Person = 'person name',
#Unit = 'some unit';
Then, event_info will contain this exact text of the EXEC query. I mean, exactly this text above. EXEC [dbo].[LoggingTest] #DateBegin = '2019-01-01', #DateEnd = '2020-01-01', #Person = 'person name', #Unit = 'some unit'; Obviously, it contains values of parameters and there is no need to parse anything or do any other tricks. It is enough for logging.
Cool. Event type will be "Language Event".
Unfortunately, if you call your stored procedure "properly" using RPC, which usually happens in, say, C# code, then all you'll get is just the name of the stored procedure in
the event_info. Something like this:
YourDatabaseName.dbo.LoggingTest
There will be no mentioning of parameters and/or their values :-( Event type will be "RPC Event".
The parameters column is smallint and always contained 0 in my tests. Docs are not clear on how to make sense of this.
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.