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

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.

Related

Set table based on stored procedure parameter

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.

View results of Stored Procedure

I have a stored procedure that executes a dynamically built string.
It unions several select statements from a constantly changing schema on another server I have no control over (hence the dynamic string). I want to be able to access the results of this procedure from a view, but this is where I get stuck.
I created a stored procedure (code below) which outputs my results to a table, but I want to be able to perform joins, etc. so it would be really convenient if there was some way to wrap this into a view- I'm thinking a table valued function, but I haven't quite figured out how to get the dynamic SQL into a function... any help is appreciated!
CREATE PROCEDURE [dbo].[usp_test]
AS
SET NOCOUNT ON;
DECLARE #sql VARCHAR(MAX)
SELECT #sql = ISNULL(#sql+'
','')+'SELECT TOP (900) *
FROM OPENQUERY(Linked_Server,
'SELECT col1, col2, col3
FROM dbo.'+ tableName+'
+'
UNION' FROM dbo.ls_views
--dbo.ls_views is a view with the pertinent views/table names from the other server.
Set #sql = #sql+ '
Select top (0) ''1'', ''2'',''3'' from sys.tables '
--last select statement is to end multiple unions... not sure if there is a better way, but this works.
--PRINT (#sql);
--EXEC (#sql);
EXECUTE sp_Executesql #sql
GO
Can you have SQL Agent execute this periodically and dump the results into another table/set of tables in your db?
Then you could use it as a straight join in whatever query you want.

SQL Dynamic SELECT statement from values stored in a table

I have been researching this for a couple of days and feel like I am going around in circles. I have basic knowledge of SQL but there are many areas I do not understand.
I have a table that stores the names and fields of all the other tables in my database.
tblFields
===================================================
TableName FieldName BookmarkName
---------------------------------------------------
Customer FirstName CustomerFirstName
Customer LastName CustomerLastName
Customer DOB CustomerDOB
I want to write a SELECT statement like the following but i am unable to get it work:
SELECT (SELECT [FieldName] FROM [TableName]) FROM tblFields
Is this possible? The application I have developed requires this for user customization of reports.
If i understand what you are trying to do, i think this will help you. It is not pretty and it works for SQL Server 2005 and above, but maybe this is what you are looking for:
declare #tableName nvarchar(100)
declare #sqlQuery nvarchar(max)
declare #fields varchar(500)
set #tableName = 'YourTableName'
set #fields = ''
select #fields = #fields + QUOTENAME(t.fieldname) + ',' from (
select distinct fieldname from tblfields where tablename = #tableName)t
set #sqlQuery = 'select ' + left(#fields, LEN(#fields)-1) + ' from ' + QUOTENAME(#tableName)
execute sp_executesql #sqlQuery
Edit: As Martin suggested, i edited so that the columns and tablename are using QUOTENAME
If I understand correctly what you are trying to do, you are probably better off doing this as two separate queries from your program. One which gets the fields you want to select which you then use in your program to build up the second query which actually gets the data.
If it must be done entirely in SQL, then you will need to tell us what database you are using. If it is SQL Server, you might be able to user a cursor over the first query to build up the second query which you then execute with the sp_executesql stored procedure. But doing doing it outside of SQL would be recommended.

Using Parameter Values In SQL Statement

I am trying to write a database script (SQL Server 2008) which will copy information from database tables on one server to corresponding tables in another database on a different server.
I have read that the correct way to do this is to use a sql statement in a format similar to the following:
INSERT INTO <linked_server>.<database>.<owner>.<table_name> SELECT * FROM <linked_server>.<database>.<owner>.<table_name>
As there will be several tables being copied, I would like to declare variables at the top of the script to allow the user to specify the names of each server and database that are to be used. These could then be used throughout the script. However, I am not sure how to use the variable values in the actual SQL statements. What I want to achieve is something like the following:
DECLARE #SERVER_FROM AS NVARCHAR(50) = 'ServerFrom'
DECLARE #DATABASE_FROM AS NVARCHAR(50) = 'DatabaseTo'
DECLARE #SERVER_TO AS NVARCHAR(50) = 'ServerTo'
DECLARE #DATABASE_TO AS NVARCHAR(50) = 'DatabaseTo'
INSERT INTO #SERVER_TO.#DATABASE_TO.dbo.TableName SELECT * FROM #SERVER_FROM.#DATABASE_FROM.dbo.TableName
...
How should I use the # variables in this code in order for it to work correctly?
Additionally, do you think my method above is correct for what I am trying to achieve and should I be using NVARCHAR(50) as my variable type or something else?
Thanks
There is probably a better way to do this, but what you are probably trying to do in your example is what's called dynamic SQL where you treat the statement as a string and the execute it. This would be section #2 here:
http://www.mssqltips.com/tip.asp?tip=1160
There are some major downsides to dynamic SQL. You see a couple other approaches that might be better in that article.
If you want to execute a dynamically generated query then you have to use sp_ExecuteSQL
HTH
For the nvarchar(50) - you'd be better using sysname. This is a synonym in SQL Server (for nvarchar(128)) and represents the maximum length of an object identifier.
have a look at http://msdn.microsoft.com/en-us/library/ms188001.aspx - sp_executesql takes a parameter that is a string and executes the sql in that string. so you'd need to concatenate #SERVER_FROM and other params with the INSERT INTO part to make the entire sql statement, and then pass to sp_executesql.
nvarchar(50) is fine, unless your server/database names are longer than that :)
You can create the select statement by concatenating all the information together and then use sp_executesql
so:
sp_executesql 'INSERT INTO ' + #SERVER_TO + '.' + #DATABASE_TO +
'.dbo.TableName SELECT * FROM ' + #SERVER_FROM + '.' +
#DATABASE_FROM+'.dbo.TableName'
I like to make templates for dynamic SQL things like this - it's a lot easier to maintain complex statements and also sometimes easier to handle nested quotes - and definitely easier when terms need to be repeated in multiple places (like column lists):
DECLARE #sql AS nvarchar(max);
SET #sql = 'INSERT INTO {#SERVER_TO}.{#DATABASE_TO}.dbo.TableName
SELECT *
FROM {#SERVER_FROM}.{#DATABASE_FROM}.dbo.TableName'
SET #sql = REPLACE(#sql, '{#SERVER_TO}', QUOTENAME(#SERVER_TO))
SET #sql = REPLACE(#sql, '{#DATABASE_TO}', QUOTENAME(#DATABASE_TO))
SET #sql = REPLACE(#sql, '{#SERVER_FROM}', QUOTENAME(#SERVER_FROM))
SET #sql = REPLACE(#sql, '{#DATABASE_FROM}', QUOTENAME(#DATABASE_FROM))
EXEC(#sql)

SQL Server: Sanitizing #param against injection attacks

For the sake of argument, let's just say I have to create a local variable containing a SQL query that has an INSERT:
DECLARE #insert NVARCHAR(MAX)
SELECT #insert = 'INSERT INTO [dbo].[' + #table + '] VALUES...
EXEC (#insert)
This INSERT is also going to contain a column value:
DECLARE #insert NVARCHAR(MAX)
SELECT #insert =
'INSERT INTO [dbo].[' + #table + '] VALUES (N''' + #message + ''')'
EXEC (#insert)
Now, I'm obviously concerned about an injection attack, and would like to ensure that #message's value can't make #insert's value malicious or malformed as a query to EXEC.
This brings us to my question: is escaping the ' characters in #message sufficient? Are there any other characters that could appear in #message that could escape out?
Example:
DECLARE #insert NVARCHAR(MAX)
SELECT #message = REPLACE(#message,'''','''''')
SELECT #insert =
'INSERT INTO [dbo].[' + #table + '] VALUES (N''' + #message + ''')'
EXEC (#insert)
(When I say "have to", this is because my query is in a stored procedure, and this stored procedure accepts #table, which is the destination table to INSERT into. I'm not interested in discussing my architecture or why the table to INSERT into is "dynamically" specified via a procedure parameter. Please refrain from commenting on this unless there's another way besides EXEC()ing a query to specify a table to INSERT into when then table name is received as a procedure parameter.)
Use sp_executesql and the built-in quotename(). This article, The Curse and Blessings of Dynamic SQL, is pretty much the definitive reference.
You could first query the schema information with regular T-SQL and make sure the table name exists first. This way, if it's malformed SQL, it won't execute as code. It will just be a VARCHAR table name.
DECLARE #Table AS VARCHAR(MAX)
DECLARE #Exists AS BIT
SET #Table = 'Vicious malformed dynamic SQL'
SELECT #Exists = COUNT(TABLE_NAME)
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #Table
IF (#Exists = 1)
BEGIN
PRINT 'Table exists'
-- Execute dynamic SQL.
END
ELSE
PRINT 'Invalid table'
(Or simply use IF EXISTS (SELECT ....) )
Rather than calling EXEC(#somesql), I suggest using the sp_executesql stored procedure. Specifically, this allows you to pass parameters, and the system will check that the parameters are valid.
Apparently there's a 128-length limit to quotename(), even in 2008 according to my test, since it expects a SQL identifier. The reference suggests creating a quotestring() function, which does the same thing as:
REPLACE(#variable,'''','''''')
Therefore I am proposing that the answer is to create a function out of the REPLACE() above, like so:
CREATE FUNCTION quotestring(#string nvarchar(MAX))
RETURNS nvarchar(MAX) AS
BEGIN
RETURN(REPLACE(#string,'''',''''''))
END
...Unless I've misunderstood something.
When writing dynamic SQL you'll want to parameterise as much as possible, and only resort to character escaping when you absolutely have to. You can't parameterise #table, but you can parameterise #message.
DECLARE #insert NVARCHAR(MAX)
set #insert = 'INSERT INTO [dbo].' + quotename(#table) + ' values(#message)'
exec sys.sp_executesql #insert, N'#message nvarchar(max)', #message = #inMessage;
There are a lot of ways attackers can exploit dynamic SQL, including buffer length attacks and using unicode equivalent characters. I encountered an example once where escaping the single quote char still had a vulnerability where one of the unicode equivalents of the quote char could be passed in. Part of the software stack was doing a unicode to ascii conversion, so it was possible to inject a quote back in after they were escaped. Ouch.