Call stored procedure for each XML element without cursors - sql-server

I have this stored procedure in SQL Server:
alter PROCEDURE [dbo].[spSendLogLinesAsXML]
(
#device_id varchar(128),
#application_name VARCHAR(64),
#application_user_name VARCHAR(6),
#log_lines_xml XML
)
AS
BEGIN
DECLARE
#ixml INT,
#log_line_dt DATETIME,
#log_line_message varchar(max)
EXEC sp_xml_preparedocument #ixml OUTPUT,
#log_lines_xml
SELECT #log_line_dt = dt,
#log_line_message = data
FROM OPENXML(#ixml, '/lines/line', 3) WITH (
dt DATETIME,
data varchar(max)
)
--I want to do the following for each line element
EXEC spSendLogLine
#device_id = #device_id,
#application_name = #application_name,
#application_user_name = #application_user_name,
#log_line_dt = #log_line_dt,
#log_line_message = #log_line_message
EXEC sp_xml_removedocument #ixml
return -100
END
I call the stored procedure like this:
EXEC #return_value = [dbo].[spSendLogLinesAsXML]
#device_id = N'devid123',
#application_name = N'CJA App 1',
#application_user_name = N'anatoli',
#log_lines_xml = '<lines><line><dt>2013-03-01T13:00:00</dt><data>Something happened and it was logged</data></line><line><dt>2013-03-01T13:01:00</dt><data>Oh my god the building is burning and people are dying</data></line></lines>'
How can I modify my stored procedure to call spSendLogLine for each line element?
Edit: According to SQL - Call Stored Procedure for each record cursors are bad. So I want to know a better way. I don't mind how much my stored procedure is changed to achieve this, as long as it ends up working properly and is nice.

alter PROCEDURE [dbo].[spSendLogLinesAsXML]
(
#device_id varchar(128),
#application_name VARCHAR(64),
#application_user_name VARCHAR(6),
#log_lines_xml XML
)
AS
BEGIN
declare #log_line_dt datetime,
#log_line_message varchar(1024)
declare #curse cursor
set #curse = cursor fast_forward for
select n.d.value('dt[1]', 'datetime') as log_line_dt, n.d.value('data[1]', 'varchar(1024)') as log_line_message
from #log_lines_xml.nodes('/lines/line') n(d)
open #curse
fetch next from #curse into #log_line_dt, #log_line_message
while (##fetch_status = 0)
begin
EXEC spSendLogLine
#device_id = #device_id,
#application_name = #application_name,
#application_user_name = #application_user_name,
#log_line_dt = #log_line_dt,
#log_line_message = #log_line_message
fetch next from #curse into #log_line_dt, #log_line_message
end
close #curse;
return -100
END
GO

This is what I ended up with, following muhmud's answer. Still wish someone would tell me how to do it sans cursors.
ALTER PROCEDURE [dbo].[spSendLogLinesAsXML]
(
#device_id VARCHAR(128),
#application_name VARCHAR(64),
#application_user_name VARCHAR(6),
#log_lines_xml XML
)
AS
BEGIN
DECLARE #ixml INT,
#log_line_dt DATETIME,
#log_line_message VARCHAR(max),
#cursor CURSOR
SET #cursor = CURSOR FAST_FORWARD
FOR
SELECT n.d.value('dt[1]', 'datetime') AS log_line_dt,
n.d.value('data[1]', 'varchar(max)') AS log_line_message
FROM #log_lines_xml.nodes('/lines/line') n(d)
OPEN #cursor
FETCH NEXT
FROM #cursor
INTO #log_line_dt,
#log_line_message
WHILE (##fetch_status = 0)
BEGIN
EXEC spSendLogLine #device_id = #device_id,
#application_name = #application_name,
#application_user_name = #application_user_name,
#log_line_dt = #log_line_dt,
#log_line_message = #log_line_message
FETCH NEXT
FROM #cursor
INTO #log_line_dt,
#log_line_message
END
RETURN - 100
END

If you're using SQL Server 2008 or newer, you could do something like this:
define a user-defined table type that holds all the columns that are relevant to your procedure:
CREATE TYPE dbo.SendLogLineType
AS TABLE (LineDate DATETIME2(3), LineData VARCHAR(200), DeviceID VARCHAR(128),
ApplicationName VARCHAR(64), AppUserName VARCHAR(6) )
then, extract your lines into that table, and add the other "fixed" parameters (this happens inside your [spSendLogLinesAsXML] procedure):
DECLARE #InputTable dbo.SendLogLineType
INSERT INTO #InputTable(LineDate, LineData, DeviceID, ApplicationName, AppUserName)
SELECT
XLine.value('(dt)[1]', 'DATETIME2'),
XLine.value('(data)[1]', 'varchar(200)'),
#device_id,
#application_name,
#application_user_name
FROM
#log_lines_xml.nodes('/lines/line') AS XTbl(XLine)
and lastly, change your spSendLogLine procedure to accept a table valued parameter of that defined table type and change its logic so that it will iterate over all rows that get passed into it:
CREATE PROCEDURE spSendLogLine
(#SendLogLineData dbo.SendLogLineType READONLY)
AS
BEGIN
-- iterate over all rows in #SendLogLineData and send out those e-mails!
END
Read more about Table-Valued Parameters on the relevant MSDN SQL Server Books Online documentation page

Related

How to pass a view name to a stored procedure in SQL Server 2014

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

How do I pass an existing table to a procedure? How do I use this... table-valued parameters thingys? look at my code below please

I have many tables that need ID scramblers, so:
CREATE PROCEDURE SP_generateUniqueID ( -- pass table here somehow -- )
AS
BEGIN
DECLARE #ID varchar(100) -- NEW ID.
DECLARE #isIDInUse tinyint -- BOOLEAN YES/NO.
SET #isIDInUse=1
WHILE(#isIDInUse=1) -- KEEP GENERATING TILL YOU FIND ONE:
BEGIN
SET #ID= dbo.generateID('aA1a1') -- GENERATES ID. doesn't matter how.
IF (#ID NOT IN (#passedTable)) -- DOES #ID EXIST ALREADY?
/*(SEARCHES THE PASSED TABLE! Which its size will be 1XN)*/
SET #isIDInUse=0 -- NO, YOU CAN USE.
END
RETURN #ID
END
I can't make the passing of the existing table go smoothly...
I want to be able to insert any table that uses IDs.
Any suggestion?
I would advise you REALLY look hard into better solutions for this issue. You will be hitting your table/index with every iteration of the new ID that you generate. What is wrong with an auto-incrementing integer value:
create table IDs (ID int identity(1,1))
(also, SQL Server has bit data types for boolean values. No need for your tinyint)
That aside, the only way I think you can do this your way is with dynamic SQL. Using the script below you should be able to see how you can pass in your schema.table to the stored procedure and within the procedure define your ID to be inserted in to the checking loop:
create table a(ID nvarchar(100)) insert into a values('1'),('2'),('3'),('4'),('5')
create table b(ID nvarchar(100)) insert into b values('6'),('7'),('8'),('9'),('10')
declare #Table nvarchar(100) = 'dbo.a'
declare #ID nvarchar(100) = '6'
declare #IDinUse bit = 0
declare #sql nvarchar(max) = 'if exists(select ID from ' + #Table + ' where ID = #ID) select #IDinUse = 1 else select #IDinUse = 0'
exec sp_executesql #sql, N'#ID nvarchar(100), #IDinUse bit output', #ID = #ID, #IDinUse = #IDinUse output
select #IDinUse as IDinUse
go
declare #Table nvarchar(100) = 'dbo.b'
declare #ID nvarchar(100) = '6'
declare #IDinUse bit = 0
declare #sql nvarchar(max) = 'if exists(select ID from ' + #Table + ' where ID = #ID) select #IDinUse = 1 else select #IDinUse = 0'
exec sp_executesql #sql, N'#ID nvarchar(100), #IDinUse bit output', #ID = #ID, #IDinUse = #IDinUse output
select #IDinUse as IDinUse

Storing output of a stored procedure into a variable when the stored procedure is called with in another stored procedure

I am executing a stored procedure within another stored procedure and then want to store the result of the inner stored procedure into a variable to use it later. The result of the inner stored procedure will be a varchar value. How can I set a variable to the result of a stored procedure ?
ALTER PROCEDURE [dbo].[mvc_Formulas]
#Fund_ID nvarchar(max),
#Start_Dated datetime,
#End_Dated datetime
AS
DECLARE #FormulaType int
DECLARE #XFund_ID bigint
DECLARE #ReturningTable TABLE(fundname varchar(100), zRETURN nvarchar(100))
DECLARE #zRETURN nvarchar(100)
DECLARE #zSD nvarchar(100)
DECLARE #FUNDS TABLE(FundId BIGINT)
INSERT INTO #FUNDS
SELECT item
FROM dbo.SplitString(#Fund_ID, ',')
DECLARE #MyCursor CURSOR;
DECLARE #CurFund BIGINT;
DECLARE #FundName NVARCHAR(100);
BEGIN
SET #MyCursor = CURSOR FOR
SELECT FundId FROM #FUNDS
OPEN #MyCursor
FETCH NEXT FROM #MyCursor INTO #CurFund
WHILE ##FETCH_STATUS = 0
BEGIN
SET #FundName = (SELECT FundName FROM FUNDS WHERE Fund_ID = #CurFund)
--inner SP--------------------------------------
SET #zRETURN = EXEC [dbo].[Fund_Performance_Graph] #Start_Dated, #End_Dated, #CurFund
-----------------------------------------------------
INSERT INTO #ReturningTable
SELECT #FundName, #zReturn
FETCH NEXT FROM #MyCursor INTO #CurFund
END
SELECT * FROM #ReturningTable
END
ALTER PROCEDURE [dbo].[mvc_Formulas]
#Fund_ID nvarchar(max),
#Start_Dated datetime,
#End_Dated datetime,
#Result varchar(1000) OUTPUT

How to pass output parameter to a Stored Procedure?

I have written a stored procedure with the following format:
ALTER PROCEDURE usp_data_migration
(#sourceDatabase varchar(50),
#sourceTable varchar(50),
#targetDatabase varchar(50),
#targetTable varchar(50),
#finaloutput varchar(max) output)
AS
BEGIN
----Set of SQL Blocks
END
Then, I am executing the procedure:
DECLARE #finaloutput1 varchar(300)
EXEC usp_data_migration 'Yousuf', 'emp', '[City Branch]', 'emp_tgt', #finaloutput1 output
SELECT #finaloutput1
By executing this way I don't proper output.
When I execute this way:
DECLARE #finaloutput1 varchar(300)
EXEC usp_data_migration #sourceDatabase = 'Yousuf',
#sourceTable = 'emp',
#targetDatabase = '[City Branch]',
#targetTable = 'emp_tgt',
#finaloutput1 output
SELECT #finaloutput1
I get an error message saying:
Msg 119, Level 15, State 1, Line 41
Must pass parameter number 5 and subsequent parameters as '#name = value'. After the form '#name = value' has been used, all subsequent parameters must be passed in the form '#name = value'.
And if I removed my output parameter and execute the procedure, I get my desired output but I am not able to get my result as an output.
EXEC usp_data_migration #sourceDatabase = 'Yousuf',
#sourceTable = 'emp',
#targetDatabase = '[City Branch]',
#targetTable = 'emp_tgt'
What should I do?
Thanks in advance.
The error message is self-explanatory - you should name all of your parameters.
DECLARE #finaloutput1 varchar(300);
EXEC dbo.usp_data_migration -- always use schema prefix
#sourceDatabase = 'Yousuf',
#sourceTable = 'emp',
#targetDatabase = '[City Branch]',
#targetTable = 'emp_tgt',
#finaloutput = #finaloutput1 OUTPUT;
SELECT #finaloutput1;
You have to Select like this
Example 1
create procedure p1
(
#id INT,
#name varchar(20) OUTPUT,
#company varchar(20) OUTPUT
)
AS
BEGIN
Set #name = 'name'
Set #company = 'company'
select #name , #company from table1 where id = #id;
END
GO
Example 2
CREATE PROCEDURE Myproc
#parm varchar(10),
#parm1OUT varchar(30) OUTPUT,
#parm2OUT varchar(30) OUTPUT
AS
SELECT #parm1OUT='parm 1' + #parm
SELECT #parm2OUT='parm 2' + #parm
GO
DECLARE #SQLString NVARCHAR(500)
DECLARE #ParmDefinition NVARCHAR(500)
DECLARE #parmIN VARCHAR(10)
DECLARE #parmRET1 VARCHAR(30)
DECLARE #parmRET2 VARCHAR(30)
SET #parmIN=' returned'
SET #SQLString=N'EXEC Myproc #parm,
#parm1OUT OUTPUT, #parm2OUT OUTPUT'
SET #ParmDefinition=N'#parm varchar(10),
#parm1OUT varchar(30) OUTPUT,
#parm2OUT varchar(30) OUTPUT'
EXECUTE sp_executesql
#SQLString,
#ParmDefinition,
#parm=#parmIN,
#parm1OUT=#parmRET1 OUTPUT,#parm2OUT=#parmRET2 OUTPUT
SELECT #parmRET1 AS "parameter 1", #parmRET2 AS "parameter 2"
go
drop procedure Myproc
Please refer more here
Simple Example:
create procedure proc2 #var int out,#var2 varchar(10) out
as
begin
set #var=(select max(id) from customer);
set #var2=(select name from customer where id=#var);
end
declare #maxid int;
declare #maxname varchar(10);
exec proc2 #maxid out,#maxname out;
select #maxid,#maxname;

Execute stored procedure with an Output parameter?

I have a stored procedure that I am trying to test. I am trying to test it through SQL Management Studio. In order to run this test I enter ...
exec my_stored_procedure 'param1Value', 'param2Value'
The final parameter is an output parameter. However, I do not know how to test a stored procedure with output parameters.
How do I run a stored procedure with an output parameter?
The easy way is to right-click on the procedure in Sql Server Management Studio (SSMS), select 'Execute stored procedure..." and add values for the input parameters as prompted. SSMS will then generate the code to run the procedure in a new query window, and execute it for you. You can study the generated code to see how it is done.
you can do this :
declare #rowCount int
exec yourStoredProcedureName #outputparameterspOf = #rowCount output
Return val from procedure
ALTER PROCEDURE testme #input VARCHAR(10),
#output VARCHAR(20) output
AS
BEGIN
IF #input >= '1'
BEGIN
SET #output = 'i am back';
RETURN;
END
END
DECLARE #get VARCHAR(20);
EXEC testme
'1',
#get output
SELECT #get
Check this, where the first two parameters are input parameters and the 3rd is an Output parameter in the Procedure definition.
DECLARE #PK_Code INT;
EXEC USP_Validate_Login 'ID', 'PWD', #PK_Code OUTPUT
SELECT #PK_Code
Procedure Example :
Create Procedure [dbo].[test]
#Name varchar(100),
#ID int Output
As
Begin
SELECT #ID = UserID from tbl_UserMaster where Name = #Name
Return;
END
How to call this procedure
Declare #ID int
EXECUTE [dbo].[test] 'Abhishek',#ID OUTPUT
PRINT #ID
From https://learn.microsoft.com/en-US/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql (originally http://support.microsoft.com/kb/262499)
CREATE PROCEDURE Myproc
#parm varchar(10),
**#parm1OUT varchar(30) OUTPUT**,
**#parm2OUT varchar(30) OUTPUT**
AS
SELECT #parm1OUT='parm 1' + #parm
SELECT #parm2OUT='parm 2' + #parm
GO
DECLARE #SQLString NVARCHAR(500)
DECLARE #ParmDefinition NVARCHAR(500)
DECLARE #parmIN VARCHAR(10)
DECLARE #parmRET1 VARCHAR(30)
DECLARE #parmRET2 VARCHAR(30)
SET #parmIN=' returned'
SET #SQLString=N'EXEC Myproc #parm,
#parm1OUT OUTPUT, #parm2OUT OUTPUT'
SET #ParmDefinition=N'#parm varchar(10),
#parm1OUT varchar(30) OUTPUT,
#parm2OUT varchar(30) OUTPUT'
EXECUTE sp_executesql
#SQLString,
#ParmDefinition,
#parm=#parmIN,
#parm1OUT=#parmRET1 OUTPUT,#parm2OUT=#parmRET2 OUTPUT
SELECT #parmRET1 AS "parameter 1", #parmRET2 AS "parameter 2"
GO
DROP PROCEDURE Myproc
First, declare the output variable:
DECLARE #MyOutputParameter INT;
Then, execute the stored procedure, and you can do it without parameter's names, like this:
EXEC my_stored_procedure 'param1Value', #MyOutputParameter OUTPUT
or with parameter's names:
EXEC my_stored_procedure #param1 = 'param1Value', #myoutput = #MyOutputParameter OUTPUT
And finally, you can see the output result by doing a SELECT:
SELECT #MyOutputParameter
With this query you can execute any stored procedure (with or without an output parameter):
DECLARE #temp varchar(100)
EXEC my_sp
#parameter1 = 1,
#parameter2 = 2,
#parameter3 = #temp output,
#parameter4 = 3,
#parameter5 = 4
PRINT #temp
Here the datatype of #temp should be the same as #parameter3 within your Stored Procedure.
How about this? It's extremely simplified:
The SPROC below has an output parameter of #ParentProductID
We want to select the value of the output of #ParentProductID into #MyParentProductID which is declared below.
Here's the Code:
declare #MyParentProductID int
exec p_CheckSplitProduct #ProductId = 4077, #ParentProductID = #MyParentProductID output
select #MyParentProductID
Try this; it's working fine for the multiple output parameter:
CREATE PROCEDURE [endicia].[credentialLookup]
#accountNumber varchar(20),
#login varchar(20) output,
#password varchar(50) output
AS
BEGIN
SET NOCOUNT ON;
SELECT top 1 #login = [carrierLogin],#password = [carrierPassword]
FROM [carrier_account] where carrierLogin = #accountNumber
order by clientId, id
END
Try for the result:
SELECT *FROM [carrier_account]
DECLARE #login varchar(20),#password varchar(50)
exec [endicia].[credentialLookup] '588251',#login OUTPUT,#password OUTPUT
SELECT 'login'=#login,'password'=#password
CREATE PROCEDURE DBO.MY_STORED_PROCEDURE
(#PARAM1VALUE INT,
#PARAM2VALUE INT,
#OUTPARAM VARCHAR(20) OUT)
AS
BEGIN
SELECT * FROM DBO.PARAMTABLENAME WHERE PARAM1VALUE=#PARAM1VALUE
END
DECLARE #OUTPARAM2 VARCHAR(20)
EXEC DBO.MY_STORED_PROCEDURE 1,#OUTPARAM2 OUT
PRINT #OUTPARAM2
Here is the stored procedure
create procedure sp1
(
#id as int,
#name as nvarchar(20) out
)
as
begin
select #name=name from employee where id=#id
end
And here is the way to execute the procedure
declare #name1 nvarchar(10)
exec sp1 1,#name1 out
print #name1
Please check below example to get output variable value by executing a stored procedure.
DECLARE #return_value int,
#Ouput1 int,
#Ouput2 int,
#Ouput3 int
EXEC #return_value = 'Your Sp Name'
#Param1 = value1,
#Ouput1 = #Ouput1 OUTPUT,
#Ouput2 = #Ouput2 OUTPUT,
#Ouput3 = #Ouput3 OUTPUT
SELECT #Ouput1 as N'#Ouput1',
#Ouput2 as N'#Ouput2',
#Ouput3 as N'#Ouput3'
Here is the definition of the stored_proc:
create proc product(#a int,#b int)
as
return #a * #b
And, this is executing it from Python:
conn = pyodbc.connect('...')
cursor = conn.cursor()
sql = """
SET NOCOUNT ON
declare #r float
exec #r=dbo.product 5,4
select #r
"""
result = cursor.execute(sql)
print (result.fetchall())

Resources