Cannot get output variable from stored procedure when procedure written in dynamic sql - sql

I am writing a procedure to produce an int output variable, but I'm not sure how to do this using dynamic sql. If I execute the below procedure I get the #AnlyNum value displayed in the results screen, but I just want #AnlyNum variable set with a value so I can use it. Thank you.
Create procedure [dbo].[sp_test] #Db varchar(50), #RwNum int, #AnlyNum int output
As
Begin
Declare #Sql nvarchar(max) =
'Select ''#AnlyNum'' = (Select AnlyId From '+#Db+'..Test order by AnlyId desc OFFSET '+convert(varchar(10),#RwNum)+' rows fetch next 1 rows only)'
End
exec(#Sql)

This removes SQL injection concerns by properly escaping the database name and also dynamically executing against that database instead of embedding the database name in the command. Also, you don't need #RwNum to be dynamic.
CREATE PROCEDURE dbo.test
#Db sysname,
#RwNum int,
#AnlyNum int output
AS
BEGIN
SET NOCOUNT ON;
DECLARE #exec nvarchar(max) = QUOTENAME(#Db) + N'.sys.sp_executesql',
#sql nvarchar(max) = N'SELECT #AnlyNum = AnlyId
From dbo.Test order by AnlyId desc
OFFSET #RwNum rows fetch next 1 rows only);';
EXEC #exec #sql, N'#AnlyNum int output, #RwNum int',
#AnlyNum output, #RwNum;
END

Related

How to add the column and than update the column in sql server using procedure

i have created the table called test_exam
create table test_exam(id int,age int)
than i want through procedure add column called name and update it value.
i have made the procedure
create procedure [dbo].[abcd1] #tablename sysname
as
begin
declare #query nvarchar(4000)
declare #name_test varchar(20)
set #query = N'select top 10 * from transorg_DW.dbo.'+#tablename
print #query
alter table test_exam add name varchar(20);
update test_exam set name='shyam' where id=1;
exec sp_executesql
end
its through an error
invalid column name.
how to rectify this error?
You should call it dynamically and in separate, I think, it will for the column at the compile time so it is displaying error. For now you can do the following
DECLARE #sql NVARCHAR(500)
SET #sql = ' alter table purchase add stat int'
EXEC sp_executesql #sql
SET #sql = ' update purchase set stat=0'
EXEC sp_executesql #sql
Note: Please check the column existence if you would need to call multiple times
When you add any column to existing table then allow null or provide some default value.
And also prevent the DDL command from being executed more then once, otherwise it will throw error on next execution.
And for update its throw error because name column didn't exist while creating the proc. Update statement must as dynamic query.
create procedure [dbo].[abcd1] #tablename sysname
as
begin
declare #query nvarchar(4000)
declare #name_test varchar(20)
set #query = N'select top 10 * from transorg_DW.dbo.'+#tablename
print #query
if not exists(select * from sys.columns c join sys.tables t on c.object_id=t.object_id
where t.name='test_exam' and c.name='name')
begin
--new column added must be as null value or default value
alter table test_exam
add name varchar(20) null;
end
declare #query1 nvarchar(4000)
set #query1='update test_exam set name=''shyam'' where id=1';
exec (#query1)
exec sp_executesql
end
Enjoy hope this will help.

Set a cursor on a table inside a table variable

All,
Trying to set a cursor on a table value inside a table variable, but it does not work. can anyone comment on how I can fix this?
** the code below is called from another stored procedure which provides the value for the tablename variable **
ALTER PROCEDURE [dbo].[usrSetLTDNormDist]
-- Add the parameters for the stored procedure here
#TableName Sysname,
---...
DECLARE #SQLCommand1 NVARCHAR(MAX) = N'
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from #TableName'
EXECUTE dbo.sp_executesql #sqlCommand1
-- Open Cursor
Open #RecCursor1
Fetch Next From #RecCursor1
Into #Volume, #TransDate
---...
Add PRINT #SQLCommand1 between the DECLARE and EXECUTE statements to review what is actually being executed. Based on your code snippet, you will see
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from #TableName
...that is, the value you set in #TableName is not automagically added to the script. Here's the way I write these things:
DECLARE #SQLCommand1 NVARCHAR(MAX)
SET #SQLCommand1 = replace(N'
Set #RecCursor1 = Cursor For
Select [Volume], [TRANSDATE] from <#TableName>'
,'<#TableName>', #TableName)
PRINT #SQLCommand1
EXECUTE dbo.sp_executesql #sqlCommand1
I use the < > characters to make the replaced values stand out.
This script demonstrates the general technique:
create table T (ID int not null)
go
insert into T(ID) values (99)
go
declare #TableName sysname
declare #ID int
set #TableName = 'T'
declare #SQL nvarchar(max) = N'declare boris cursor for select ID from ' +
QUOTENAME(#TableName)
exec sp_executesql #SQL
open boris
fetch next from boris into #ID
while ##FETCH_STATUS = 0
begin
print #ID
fetch next from boris into #ID
end
close boris
deallocate boris
Producing this output:
(1 row(s) affected)
99
However, I will offer my usual caution - if you're in a situation where you want to operate against multiple tables in the same way, this is usually a sign of a broken data model. Usually there ought to be a single table with additional columns containing data that serves to differentiate the values.

Sql Server Stored Procedure dynamically select table name

i have this stored procedure, I want to dynamically select table name based on the variable passing ie #Practice_Short_Name
Create procedure [dbo].[GetCompleteCPTDetails]
#Practice_Short_Name varchar(50) is Null,
#Uploaded_Date varchar(30) is Null
as
begin
DECLARE #CPTtablename varchar(100)
DECLARE #vQuery NVARCHAR(100)
--Dynamically select Table name based on #practice_short_name
set #CPTtablename ='ACER_CLAIMS_MASTER_DETAIL_Hist_'+#Practice_Short_Name+''
SET #vQuery = 'select Practice_Short_Name,Service_Date_From,Carrier_Name,
Location_Description,Patient_Number,Patient_First_Name,
Patient_Last_Name,Voucher_Number,Procedure_Code,Service_Fees,
Service_Payments,Service_Adjustments,Acer_Status,Acer_Allowed_Amount
from '+#CPTtablename+'
where Uploaded_Date ='+#Uploaded_Date+' and
Practice_Short_Name ='+#Practice_Short_Name+'
order by acer_status asc, Service_Date_From desc, Patient_First_Name asc'
EXEC #vQuery
end
GO
but while running this proc it is throwing error like
"Could not find stored procedure 'select Practice_Short_Name,Service_Date_From,Carrier_Name,
Location_Description,Patient_Numb'."
can anyone explains me what i am doing wrong..
This way you call a procedure
EXEC #vQuery
but this way you run dynamic sql
EXEC (#vQuery)
so your SP should look as below
Create procedure [dbo].[GetCompleteCPTDetails]
#Practice_Short_Name varchar(50) is Null,
#Uploaded_Date varchar(30) is Null
as
begin
DECLARE #CPTtablename varchar(100)
DECLARE #vQuery NVARCHAR(100)
--Dynamically select Table name based on #practice_short_name
set #CPTtablename ='ACER_CLAIMS_MASTER_DETAIL_Hist_'+#Practice_Short_Name+''
SET #vQuery = 'select Practice_Short_Name,Service_Date_From,Carrier_Name,
Location_Description,Patient_Number,Patient_First_Name,
Patient_Last_Name,Voucher_Number,Procedure_Code,Service_Fees,
Service_Payments,Service_Adjustments,Acer_Status,Acer_Allowed_Amount
from '+#CPTtablename+'
where Uploaded_Date ='+#Uploaded_Date+' and
Practice_Short_Name ='+#Practice_Short_Name+'
order by acer_status asc, Service_Date_From desc, Patient_First_Name asc'
EXEC (#vQuery)
end
GO
The variable you use to store the query is too short to store the entire query text.
This means that the query is truncated to fix the 100 characters limit of the #vQuery variable.
Use nvarchar(max) instead of nvarchar(100).
Also, using Dynamic SQL is usually a security hazard, leaving an opening to SQL Injection attacks. You might want to re-think your design and keep all the data in the same table, instead of keeping different tables with the same structure on your datadase.
2 things need to be corrected : length of your dynamic query variable that caused you this problem. use parameterized dynamic query to prevent sql injection.
DECLARE #CPTtablename varchar(100)
DECLARE #vQuery NVARCHAR(2000) -- increased length, you can also use nvarchar(max)
--Dynamically select Table name based on #practice_short_name
SET #CPTtablename ='ACER_CLAIMS_MASTER_DETAIL_Hist_' + #Practice_Short_Name + ''
SET #vQuery = 'select Practice_Short_Name,Service_Date_From,Carrier_Name,
Location_Description,Patient_Number,Patient_First_Name,
Patient_Last_Name,Voucher_Number,Procedure_Code,Service_Fees,
Service_Payments,Service_Adjustments,Acer_Status,Acer_Allowed_Amount
from ' + #CPTtablename + '
where Uploaded_Date = #Uploaded_Date and
Practice_Short_Name = #Practice_Short_Name
order by acer_status asc, Service_Date_From desc, Patient_First_Name asc'
--dynamic query with input params
EXEC sp_executesql
#vQuery,
N'#Uploaded_Date varchar(30), #Practice_Short_Name varchar(50)',
#Uploaded_Date = #Uploaded_Date,
#Practice_Short_Name = #Practice_Short_Name

Execute Stored Procedure from Stored Procedure w/ dynamic SQL capturing output

Inside a stored procedure (A) I need to call a stored procedure (X) inside a specific database and capture the output. X returns a single value.
From what I understand I need to provide the DB name of X to the stored procedure in A and I need to use dynamic SQL to build the query on execution targeting the desired database.
What am unable to figure out is how to capture output from X in A to work with the result.
You could use sp_executesql to dynamically call your nested Stored Procedure.
DECLARE #db AS SYSNAME
DECLARE #return_value AS INT
DECLARE #output_value AS INT
DECLARE #sql AS NVARCHAR(MAX)
-- Set your DB name
SET #db = N'mydb'
/*
Use sp_executesql to dynamically pass in the db and stored procedure
to execute while also defining the values and assigning to local variables.
*/
SET #sql = N'EXEC #rtn = ' + #db + '.dbo.[your_stored_procedure] #output OUTPUT'
EXEC sp_executesql #sql
, N'#rtn AS INT, #output AS INT OUTPUT'
, #return_value = #rtn
, #output_value = #output OUTPUT
Adding to the above answer, following is the way to call stored procedures dynamically by passing parameters.
DECLARE #SpName VARCHAR(1000)
SELECT #SpName = DeleteLiveSP FROM dbo.ArchivalInfo (NOLOCK) WHERE TableName = #TableName
DECLARE #SqlString nvarchar(2000)
DECLARE #ParamDef nvarchar(2000)
SET #SqlString = N'exec '+#SpName + ' #CriteriaParam'
SET #ParamDef = N'#CriteriaParam XML'
EXECUTE sp_executesql #SqlString ,#ParamDef, #CriteriaParam = #Criteria

How can I spot in what database is a stored procedure with name 'myStoredProcedure'?

There are bunch of databases to the SQL server I am connected.
How should I query the sysobjects in order to spot in what database a stored procedure with name 'myStoredProcedure' is located ?
The query should return the database name.
Thanks
I know you are not asking for this, but I'd really download RedGate's Sql Search add-in for SSMS and use that. It allows you to find any object (proc, table, view, column, etc) on any database easily.
And it's free!
I'd give this a try:
CREATE TABLE ##DatabaseList
(
DatabaseName varchar(50)
)
EXECUTE SP_MSForEachDB 'USE [?]; INSERT INTO ##DatabaseList SELECT DB_NAME() FROM [sys].[objects] WHERE name = "MyStoredProcedure" AND type_desc = "SQL_STORED_PROCEDURE"'
SELECT * FROM ##DatabaseList
DROP TABLE ##DatabaseList
That's using the undocumented/ unsupported system stored procedure SP_MSForEachDb and writing any hits to a global temp table, then outputting the contents to the Results window before dropping the table. If you just need to know which database (or databases - there may of course be more than one) has an appropriately named SP, this should do it. If you want to use the output elsewhere as a parameter, it may take a little more work.
By the way, I'm only learning this stuff myself over the last few months so if anyone can critique the above and suggest a better way to go at it I'm happy to receive feedback. Equally, I can answer any further questions posted here to the best of my ability.
Cheers
So out of curiosity I decided to try write this myself, especially since ADG mentioned his solution was using an unsupported, undocumented procedure. This could also be expanded to take a 2nd parameter so where it checks the type = P (stored Proc) you could probably change it to look for other things like views / tables etc.
My solution is a bit long but here goes:
CREATE PROCEDURE spFindProceduresInDatabases
(
#ProcedureName NVARCHAR(99)
)
AS
BEGIN
-- Get all the database names and put them into a table
DECLARE #Db TABLE (DatabaseName Varchar(99))
INSERT INTO #Db SELECT name FROM Sys.databases
-- Declare a table to hold our results
DECLARE #results TABLE (DatabaseName VARCHAR(99))
-- Make a Loop
-- Declare a variable to be incremented
DECLARE #count INT
SET #count = 0
-- Declare the end condition
DECLARE #endCount INT
SELECT #endCount = COUNT(*) FROM #Db
-- Loop through the databases
WHILE (#count < #endCount )
BEGIN
-- Get the database we are going to look into
DECLARE #dbWeAreChecking VARCHAR(99)
SELECT TOP 1 #dbWeAreChecking = DatabaseName FROM #Db
DELETE FROM #Db WHERE DatabaseName = #dbWeAreChecking
-- Create and execute our query
DECLARE #Query NVARCHAR(3000)
SET #Query = N'SELECT #outParam = COUNT(*) FROM '+#dbWeAreChecking+'.sys.sysobjects WHERE type = ''P'' and name = #ProcedureName'
Declare #outParam INT
print (#Query)
DECLARE #ParmDefinition NVARCHAR(500)
DECLARE #IntVariable INT
SET #ParmDefinition = N'#ProcedureName VARCHAR(99),#outParam INT OUTPUT'
SET #IntVariable = 35
EXECUTE sp_executesql
#Query ,
#ParmDefinition,
#ProcedureName,
#outParam = #outParam OUTPUT
-- If we have a result insert it into the results table
If (#outParam > 0)
BEGIN
INSERT INTO #results(DatabaseName) VALUES(#dbWeAreChecking)
END
-- Increment the counter
SET #count = (#count + 1)
END
-- SELECT ALL OF THE THINGS!!!
SELECT * FROM #results
END