Set table based on stored procedure parameter - sql

We have a process that updates certain tables based on a parameter passed in, specifically a certain state. I know organizationally this problem would be eliminated by using a single table for this data, but that is not an option -- this isn't my database.
To update these tables, we run a stored procedure. The only issue is that there was a stored procedure for each state, and this made code updates horrible. In order to minimize the amount of code needing to be maintained, we wanted to move towards a single stored procedure that takes in a state parameter, and updates the correct tables. We wanted this without 50 If statements, so the only way I could think to do this was to save the SQL code as text, and then execute the string. IE:
SET #SSQL = 'UPDATE TBL_' + #STATE +' SET BLAH = FOO'
EXEC #SSQL;
I was wondering if there was a way to do this without using strings to update the correct tables based on that parameter. These stored procedures are thousands of lines long.
Thanks all!

Instead save entire script as SQL text and execute it, just update the required table using like code below as where you need and rest continue as it is
EXEC('UPDATE TBL_' + #STATE +' SET BLAH = FOO')

You could, indeed, use dynamic SQL (the exec function) - but with long, complex stored procedures, that can indeed be horrible.
When faced with a similar problem many years ago, we created the stored procedures by running a sort of "mail-merge". We'd write the procedure to work against a single table, then replace the table names with variables and used a PHP script to output a stored procedure for each table by storing the table names in a CSV file.
You could replicate that in any scripting language of your choice - it took about a day to get this to work. It had the added benefit of allowing us to easily store the stored proc templates in source code control.

You can safely use sp_executesql which is fairly more appropriate than a simple EXEC command. To do so, even with input and output parameters :
DECLARE #sql nvarchar(4000),
#tablename nvarchar(4000) = 'YOUR_TABLE_NAME',
#params nvarchar(4000),
#count int
SELECT #sql =
N' UPDATE ' + #tablename +
N' SET Bar = #Foo;' +
N' SELECT #count = ##rowcount'
SELECT #params =
N'#Foo int, ' +
N'#count int OUTPUT'
EXEC sp_executesql #sql, #params, 2, #count OUTPUT
SELECT #count [Row(s) updated]
I encourage you reading the related part of the article mentionned here.

Related

How to create a simple stored procedure with table name as an input

I am using SQL Server 2017 and I would like to create a stored procedure with a single table name as an input variable. All I want the procedure to do is update that table in a variety of ways. This project will be done twice a year, and the columns will always be the same, so I would like to try this as a stored procedure, so I do not have to highlight several lines of code and executing each time.
Is there a simple way to pass a table name through a stored procedure which updates the table (adding columns, calculating columns, replacing nulls in columns etc). In a basic example, one task would be just replaces nulls with 0s in a column. I am not sure how to set this up though. DO I have to declare every column in the table too?
CREATE PROCEDURE updteleform
#tablename TABLE
AS
BEGIN
UPDATE #tablename
SET Recog = 0
WHERE Recog IS NULL
END
GO
I'm assuming you want to update a physical table. SQL Server table variables don't work that way, rather they are a way to pass a transient result set to a stored procedure. There is no persistence if your stored procedure does not do so.
If you are looking to update the same table, then just write the procedure to work on that table.
If you are looking to update multiple tables using the same script then you should change your procedure to accept a string parameter that would be the name of the table you want it to work on and then use dynamic SQL in your stored procedure.
Something like
CREATE PROCEDURE updteleform #tablename sysname
AS
BEGIN
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'
update ' + QUOTENAME(#tablename) + '
set Recog= 0
where Recog is null;';
EXEC sp_executesql #sql;
END
GO
And then call it with something like:
EXEC updteleform #tablename = 'table1';
EXEC updteleform #tablename = 'table2';
EXEC updteleform #tablename = 'table3';
...

Need to use dynamic variables to build the 'execute sp_executesql' parameters

I have a quick question about the use of execute sp_executesql
Without going into too much details of my code that's current causing an error, i'm more interested in the different methods I could use to build the EXECUTE SP_EXECUTESQL statement
Within my STOR_PROC, I have the following (with #tTableName & #tVar as parameters being passed in)
DECLARE #strSQL nVARCHAR(1000)
DECLARE #parmDef nVARCHAR(100)
DECLARE #outDef nVARCHAR(100)
SET #strSQL = N'SELECT #nTotalOut = sum(r.TOT_SALES) FROM #tTableName
--this procedure is actually in a loop, as I need to provided total sales
--from multiple years and sales data per year is stored in its own individual table
--(ie: #tTableName = '2015_Sales_Data', '2014_Sales_Data', etc, etc)
SET #parmDef = N'#nTotalOut int output'
set #outDef = N'#nTotalOut=' + #tVar + ' OUTPUT'
execute sp_executesql #strSQL, #parmDef, #outDef
the reason why I need to dynamically define #outDef is because for each iteration, I need to store the total sales into its own variable (so #tVar will hold the actual variable names, ie: #nTotal2015, #nTotal2014, #nTotal2013, etc, etc)
At the end when this loop is done, I can do a
SELECT #nTotal2015, #nTotal2014, #nTotal2013
and display the total sales per year in 1 row
PROBLEM:
when I run my store procedure, I'm getting the following error:
Error converting data type nvarchar to int.
I've tried many different workarounds before I decided to post here, but at the end, I would like to know if i can build the EXECUTE SP_EXECUTESQL statement with dynamical variables
One thing to note: If i hardcoded the output parameter to:
execute sp_executesql #strSQL, #parmDef, #n2015Total=#n2015Total OUTPUT
then it works!
Any suggestions?
Too long for a comment.
Run the code using a single variable and stash the value in a temporary table.
declare #Totals table (year int, total decimal(18, 6));
. . .
execute sp_executesql #strSQL, #parmDef, #outDef;
insert into #Totals(year, total)
select #year, #total;
This assumes that you have a #year variable among the variables being processed.

How I can use a single stored procedure for data insertion in multiple tables in SQL Server 2008?

Suppose I have many tables in my database. Every time I will insert data in any table. So, can I use a single stored procedure for all my data insertion in every table, instead of writing one stored procedure for each table?
In this scenario, each time I will pass table name and parameters dynamically to the stored procedure. If yes, can anyone give some basic idea how to perform this? If any extra information is required, please ask.
Thanks and regards,
Rizwan Gazi.
You could work with dynamic SQL and build the insert statement on the fly. THIS IS NOT RECOMMENDED but it should solve the problem you're asking about.
(I haven't run this code, but you can see what is being accomplished here with building the insert string and then executing it)
In this procedure, you pass in the table name, columns and values you care about and fire it off in a row based operation. With some minor tweaks you would be able to make it set based as well.
create procedure dbo.TableInsert
#tablename varchar(100)
, #columnlist varchar(max)
, #valueslist varchar(max)
as
begin
declare #sql varchar(max)
set #sql =
'insert into ' + #tablename
+ '(' + #columnlist + ')'
+ ' VALUES (' + #valueslist + ')'
print(#sql)
sp_executesql (#sql)
end
go
Execution would look something like this:
exec dbo.TableInsert
#tablename = 'TestTable'
, #columnlist = 'col1, col2, col3'
, #valuelist = '1,2,3'
Text insert would be a little trickier in this version since you have to wrap it around in single quotes.
exec dbo.TableInsert
#tablename = 'TestTable'
, #columnlist = 'col1, col2, col3'
, #valuelist = '''1'',''2'',''3'''
You could do something using dynamic SQL to build a query and then run it using:
EXEC SP_EXECUTESQL(#SQL)
(Assuming MS SQL Server)
Not sure I'd recommend it though and will probably be a total nightmare to test and maintain. Having different sprocs would be easier to test and maintain going forward and would perform better as the different sprocs would have separate query plans.
If you are working in code you could use a ORM to deal with basic CRUD stuff.

Select from table with current user's schema

I am working in a database where each user has a table in their schema called FindResults
eg: MyDatabase.User1.FindResults, MyDatabase.User2.FindResults, etc.
If I run a SELECT query on this table while logged in as one of the users it works just fine. However, I have a stored procedure (MyDatabase.dbo.ReadFindResults) that tries to run a SELECT query on this table, it fails because it tries to read MyDatabase.dbo.FindResults (does not exist). I have gotten around this by using dynamic SQL, but I was hoping there was a way to avoid this.
Is there a way to tell the stored procedure to use the current user's schema or perhaps something to change the scope to allow it to find the table I want?
EDIT: Here is the code for the stored procedure
-- Returns the IDs contained in the given find results set
CREATE PROCEDURE [dbo].[ReadFindResults]
#resultsid int -- The ID of the results set
AS
BEGIN
SELECT objectid FROM FindResults WHERE resultsid = #resultsid
END
GO
In your stored procedure you can say:
SELECT cols FROM MyDatabase..FindResults;
(Leaving out the schema name.)
However this seems very error-prone, and IMHO you should either have separate, schema-bound stored procedures as well, or a single table with a column to indicate whose row it is.
A hack could be to execute as the username:
DECLARE #sql NVARCHAR(255) = N'EXECUTE AS USER = N''' + SUSER_SNAME() + '''';
EXEC sp_executesql #sql;
SELECT whatever FROM Findresults;
But I haven't had the opportunity to test this (you're better equipped to test in your scenario anyway).
It still seems like a remarkably avoidable problem in the first place, but maybe that's just me.
You could use dynamic SQL like this:
DECLARE #sql NVARCHAR(1024) = "SELECT something FROM [userName].someTable"
DECLARE #userName NVARCHAR(255) = SUSER_SNAME()
SET #sql = REPLACE(#sql, "[userName]", #userName)
EXEC sp_executesql #sql
That too is a hakish way of doing that query. More reading about dynamic SQL here: http://www.sommarskog.se/dynamic_sql.html
Im at home right now and do not have access to a SQL Server. I will edit my post in the morning when Im at work if needed.

Passing dynamic parameters to a stored procedure in SQL Server 2008

I have this procedure that executes another procedure passed by a parameter and its parameters datefrom and dateto.
CREATE procedure [dbo].[execute_proc]
#procs varchar(200),
#pdatefrom date,
#pdateto date
as
exec #procs #datefrom=#pdatefrom,#dateto=#pdateto
But I need to also pass the parameters dynamically without the need to edit them in the procedure. For example, what I am imagining is something like this
CREATE procedure [dbo].[execute_proc]
#procs varchar(200),
#params varchar(max)
as
exec #procs #params
where #params is a string like #param1=1,#param2='somethingelse'
Is there a way to do this?
It's not really clear what the point of your wrapper procedure is (auditing? debugging?), and it seems like a very awkward solution. If you explain why you want to do this, someone may have a completely different and hopefully better solution.
The biggest issue with your proposal is that you can only pass parameters as strings and that means you have to handle all the escaping, data conversion/formatting and SQL injection issues that come with dynamic SQL. It would be much better to call each procedure directly, passing correctly typed parameters from your calling code.
Having said all that, if you really want to do it then you can do something like this:
create proc dbo.ExecuteProcedure
#ProcedureName sysname,
#Parameters nvarchar(max),
#Debug bit = 0x0,
#Execute bit = 0x1
as
set nocount on
begin
declare #sql nvarchar(max)
set #sql = 'exec ' + quotename(#ProcedureName) + ' ' + #Parameters
if #Debug = 0x1 print #sql
if #Execute = 0x1 exec(#sql)
end
go
exec dbo.ExecuteProcedure 'dbo.SomeProc', '#p1 = 1, #p2 = ''themhz''s proc''', 0x1, 0x0
You should also have a look at sp_executesql, which does almost exactly what you want, but it needs to have all the parameter data types too, which you say is not possible in your scenario.
Put the stored procedure name in a varchar field in your client table
Retrieve the SP name and assign it to a parameter ( spName) when the client is chosen.
In code create a function that returns a string
function PassStoredProcedureName(spName as string) as string
return spName
end function
Set your dataset to "Stored Procedure"
Open a dataset Expression window
Enter =Code.PassStoredProcedureName(Parameters!spName.value)
When you chose a client, the spName will be assigned to the parameter. When the dataset executes, it will pass the parameter to the function, which will pass the spName to the dataset.
I use this to execute custom stored procedures for clients when the same stored procedure will not work for all clients.
Be sure to normalize the aliased field names so that data retrieval to a report does not break.
Your stored procedures should always have the same parameter requirements even if they are not needed.