SQL Server insert parameterized name table to IF EXISTS - sql-server

I want to insert #inputData to ColumnData if it doesn't exists to prevent duplicate data to a table with name as parameter #TableName. Error at the 'dbo.#TableName'.
SET #insertSQL = 'INSERT INTO '+ #TableName + ' (ColumnData) VALUES ('''+#inputData+''');'
IF NOT EXISTS (SELECT 1 FROM [dbo].[#TableName] WHERE ColumnData = #inputData)
EXECUTE(#insertData) -- EXECUTE #insertData if ColumnData is not found
I also tried this (also throws error):
SET #insertSQL = 'INSERT INTO '+ #TableName + ' (ColumnData) VALUES ('''+#inputData+''');'
IF NOT EXISTS (EXECUTE('SELECT 1 FROM [dbo]. '+#TableName ' WHERE ColumnData = ' + #inputData))
EXECUTE(#insertData) -- EXECUTE #insertData if ColumnData is not found

It look like you are splitting the dynamic SQL statement. Try something like this:
DECLARE #SQL NVARCHAR(MAX) = N'
IF NOT EXISTS (SELECT 1 FROM dbo.' + QUOTENAME(#TableName) + ' WHERE ColumnData = #inputData
INSERT INTO ' + QUOTENAME(#TableName) + ' (ColumnData) VALUES (#inputData)'
EXEC sp_executesql #SQL, N'#inputData VARCHAR(128)', #inputData = #inputData
Table name cannot be parameterized, so it must be appended. However, QUOTENAME is strongly recommended to avoid SQL injection.
On the other hand, #inputData can be sent as a parameter, that's why sp_executesql can be used.
Generally speaking, unless you are not dealing with parameters, try to always use sp_executesql instead of simple EXEC (), as EXEC forces into ugly string concatenation and possible injectable SQL statements.

Related

How do i insert or update a record based on data whicha has a apostohphe in a dynamic sql?

Here the #Data has a value with apostophe(')s . how do i update or insert a data based on the data value which is having apostophe in a dynamic sql
suppose #data has one value abc and another value abc's it throwing error for the second one
SET #SQL = ' Update '+ #ProcessCode + '_abc SET IS_IGNORING = 1 where Column_Name = '''+ #Column_Name +''' and [DATA] = ''' + #Data + ''' and Table_name = '''+ #Table_Name + ''''
Generally what i found is a manual process of adding one more apostophe but i am not really sure how to use that in a dynamic sql where not all data in the table is same, few of the data records has got this type of apostophe(')
Parameterized your query using sp_executesql
Example:
SET #SQL = 'Update ' + #ProcessCode + '_abc '
+ 'SET IS_IGNORING = 1 '
+ 'where Column_Name = #Column_Name '
+ 'and [DATA] = #Data '
+ 'and Table_name = #Table_Name '
EXEC sp_executesql #SQL,
N'#Column_Name varchar(100), #Data varchar(100), #Table_Name varchar(100)',
#Column_Name, #Data, #Table_Name
Do read up more on dynamic query and SQL Injection
You might find convenient to use parameterized queries, so you can replace static values with placeholders and then bind values to those placeholders before executing the query. It has certain advantages like better performance and helps to avoid SQL-injection attacks.
More info here: https://techcommunity.microsoft.com/t5/sql-server-blog/how-and-why-to-use-parameterized-queries/ba-p/383483

I want to write one common stored procedure to insert data in any table of my database

I have 6 table with different fields. I want to access table name dynamically. is there any idea to do it?
My code is below this is simple procedure which I want to make dynamic to use in c#. how to do it?
Create procedure [dbo].[Insert_Data] (#Id int,#FeesHead nchar(20),#Fees int,#Remarks nchar(20))
as
begin
Insert into FeesHead(ID,FeesHead,Fees,Remarks)values(#Id,#FeesHead,#Fees,#Remarks)
End
Don't go there.
It's a bad idea since you will end up with a long, inefficient stored procedure that will be vulnerable to SQL injection attacks and have performance issues.
Writing an insert stored procedure for each table is the way to go.
You wrote you have six different tables with different columns, so writing a stored procedure to handle inserts for all of them will require you to send all the parameters for all columns as well as a parameter for the table name, and a nested if...else with 6 possible paths, one for each table.
This will end up as a long, messy, poorly written code at best, bad in each parameter: security, performance, code readability and maintainability.
The only way that makes some sense to achieve such a goal is to write individual insert stored procedures for each table, and then write a stored procedure that will take all of the possible parameters and the table name and inside of it decide what insert stored procedure to execute based on the value of the table name parameter. However, you will be better off leaving conditions like these to the SQL client (your c# code in this case) then to SQL Server.
Its very easy to do.........
Just call the sql query using Data Adapter.
select TABLE_NAME from INFORMATION_SCHEMA.TABLES
As you said you need dynamic SQL like this:
Create procedure [dbo].[Insert_Data]
(
#TableName nvarchar(512),
#Values nvarchar(max)
)
BEGIN
DECLARE #SQL nvarchar(max)
SELECT #SQL = 'INSERT INTO ' + #TableName + ' VALUES (' + #Values + ')'
EXEC(#SQL)
END
Note that #Values will be like this '1, ''name'', 10.2' and with the same order of columns.
or
Create procedure [dbo].[Insert_Data]
(
#TableName nvarchar(512),
#Fields nvarchar(max),
#Values nvarchar(max)
)
BEGIN
DECLARE #SQL nvarchar(max)
SELECT #SQL = 'INSERT INTO ' + #TableName + ' (' + #Fields + ') VALUES (' + #Values + ')'
EXEC(#SQL)
END
To more ability to handle column order and remove identity columns.
As Robert Harvey mentioned it is a bad idea, anyway if you want to you can do something like....
CREATE PROCEDURE Insert_Data
#TableName SYSNAME
,#Column1 SYSNAME = NULL
,#Column2 SYSNAME = NULL
,#Column3 SYSNAME = NULL
,#Value1 NVARCHAR(100) = NULL
,#Value2 NVARCHAR(100) = NULL
,#Value3 NVARCHAR(100) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' INSERT INTO ' + QUOTENAME(#TableName)
+ N' ( '
+ STUFF(
CASE WHEN #Column1 IS NOT NULL
THEN N',' + QUOTENAME(#Column1) ELSE N'' END
+ CASE WHEN #Column2 IS NOT NULL
THEN N',' + QUOTENAME(#Column2) ELSE N'' END
+ CASE WHEN #Column3 IS NOT NULL
THEN N',' + QUOTENAME(#Column3) ELSE N'' END
,1,1,'')
+ N' ) '
+ N' VALUES ( '
+ STUFF(
CASE WHEN #Value1 IS NOT NULL
THEN N', #Value1' ELSE N'' END
+ CASE WHEN #Value2 IS NOT NULL
THEN N', #Value2' ELSE N'' END
+ CASE WHEN #Value3 IS NOT NULL
THEN N', #Value3' ELSE N'' END
,1,1,'')
+ N' ) '
Exec sp_executesql #Sql
,N'#Value1 NVARCHAR(100),#Value2 NVARCHAR(100),#Value3 NVARCHAR(100)'
,#Value1
,#Value2
,#Value3
END

Error with declaring scalar variable in dynamic SQL

I am working in SQL Server 2008. I have the following stored procedure that has a dynamic SQL statement in it:
CREATE PROCEDURE dbo.test
#list_of_codes varchar(255) = NULL,
#test_ID int = NULL
AS
DECLARE #sql1 nvarchar(max)
SELECT #sql1 =
'
INSERT INTO some_table
SELECT
some_col AS Some_Column,
#test_ID AS Testing_Identifier
FROM some_table
WHERE
testing_number_col = #test_ID
AND
test_col NOT IN (''' + REPLACE( #list_of_codes, ',', ''',''') + ''')
'
EXEC sp_executesql #sql1
'
I execute this stored procedure with the following statement:
EXEC dbo.test #list_of_codes = 'x,y,z', #test_ID = 10
When I execute this statement, an error is thrown, stating
Must declare the scalar variable ''test_ID''.
I'm sure my error is something as simple as escaping the #test_ID scalar variable in the dynamic SQL statement, but I don't see where I need to escape it (if that is really my problem). What am I doing wrong?
You are missing quotes
Considering that #test_ID is of Integer Type. If #test_ID is of varchar type then you may have to add additional quotes
SELECT #sql1 =
'
INSERT INTO some_table
SELECT
some_col AS Some_Column,
'+convert(varchar(20),#test_ID)+' AS Testing_Identifier
FROM some_table
WHERE
testing_number_col = '+ convert(varchar(20),#test_ID) + '
AND
test_col NOT IN (''' + REPLACE( #list_of_codes, ',', ''',''') + ''')
'
The dynamic part of your query knows nothing of the parameters in the outer stored procedure, so you must declare them when calling sp_executesql
This is covered in the documentation.
In your example you could use
DECLARE #sql1 nvarchar(max)
DECLARE #Params NVARCHAR(MAX) = '#test_ID INT'
SELECT #sql1 =
'
INSERT INTO some_table
SELECT
some_col AS Some_Column,
#test_ID AS Testing_Identifier
FROM some_table
WHERE
testing_number_col = #test_ID
AND
test_col NOT IN (''' + REPLACE( #list_of_codes, ',', ''',''') + ''')'
EXEC sp_executesql #sql1, #Params, #Test_ID=#Test_ID

SQL Server: executing dynamic/inline sql table name within EXEC and passing geometry as geometry parameter

I'm trying to execute an inline SQL statement within a stored procedure. I'm working with SQL Server 2008.
The problem is that I can't execute the first inline statement (with WHERE clause). It crashes because the string within EXEC(...) is dynamically created and all concatenated variables must be of type varchar.
Error that appears when calling procedure:
An expression of non-boolean type specified in a context where a
condition is expected, near 'ORDER'.
The procedure looks like:
CREATE PROCEDURE loadMyRows
#table_name nvarchar(50),
#bounding_box varchar(8000)
AS
BEGIN
-- *********************************** COMMENT *********************************
-- ** This two code lines are correct and will return true (1) or false (0), **
-- ** but they doesn't work within inline EXEC(...) **
--DECLARE #bb geometry = geometry::STGeomFromText(#bounding_box, 4326);
--select TOP(5) wkt.STWithin(#bb) AS 'bool'
-- *********************************** COMMENT *********************************
IF #bounding_box <> ''
BEGIN
DECLARE #bb geometry = geometry::STGeomFromText(#bounding_box, 4326);
EXEC(
'SELECT TOP (' + #row_limit + ') * ' +
'FROM ' + #real_table_name + ' ' +
'WHERE wkt.STWithin('+#bb+') ' + -- <-- doesn't work :-(
-- 'WHERE wkt.STWithin(geometry::STGeomFromText('''+#bounding_box+''', 4326)) ' +
-- ^^ doesn't work, too :-(
'ORDER BY id ASC '
);
END
ELSE
BEGIN
EXEC(
'SELECT TOP (' + #row_limit + ') * ' +
'FROM ' + #real_table_name + ' ' +
'ORDER BY id ASC'
);
END
END
I've found a working solution for this problem. The way the MSDN showed me was http://msdn.microsoft.com/en-US/library/ms175170.aspx. There's written:
[...] the string is executed as its own self-contained batch.
That let me know, if I want to execute a dynamic statement with a table variable as string, it's the same as I would execute the query without the EXECUTE command, like:
SELECT TOP(#row_limit) *
FROM #real_table_name
WHERE ...
ORDER BY id ASC;
And this would probably not work for the table name.
So, if I write instead:
DECLARE #sql_statement nvarchar(MAX) = 'SELECT TOP(#limit) *
FROM ' + #real_table_name + '
ORDER BY id ASC';
-- declaration of parameters for above sql
DECLARE #sql_param_def nvarchar(MAX) = '#limit int';
EXECUTE sp_executesql #sql_statement, #sql_param_def, #limit = #row_limit;
Then, this would work. This is because I define the #sql_statement simply as a concatenated string which will just resolve the dynamic table name at runtime to a string with the name of the real existing table. The #limit parameter is untouched and is still a parameter.
If we then execute the batch we only must pass a value for the #limit parameter and it works!
For the geometry parameter it works in the same way:
DECLARE #bb geometry = geometry::STGeomFromText(#bounding_box, 4326);
SET #sql_statement = 'SELECT TOP(#limit) *
FROM ' + #real_table_name + '
WHERE wkt.STWithin(#geobb) = 1
ORDER BY id ASC';
-- NOTE: This ' = 1' must be set to avoid my above described error (STWithin doesn't return a BOOLEAN!!)
-- declaration of parameters for above sql
SET #sql_param_def = '#limit int, #geobb geometry';
EXECUTE sp_executesql #sql_statement, #sql_param_def, #limit = #row_limit, #geobb = #bb;
Hope this was clear ;-)
create proc usp_insert_Proc_Into_temp
#tempTable nvarchar(10) output
as
begin
set #tempTable = '##temp'
declare #query nvarchar(200)
--Select statement
set #query = 'select 1 as A,2 as B, 3 as C into'+ ' '+#tempTable+''
exec(#query)
end
go
declare #tempTable nvarchar(10)
exec usp_insert_Proc_Into_temp #tempTable output
exec('select * from' + ' '+ #tempTable+'')
exec ('drop table'+ ' '+#tempTable+'')

How to create a UDF or View in another database that references the correct sys.objects table in the caller?

Using SQL Server 2008, I'd like to create a UDF that gives me the create date of an object. This is the code:
create function dbo.GetObjCreateDate(#objName sysname) returns datetime as
begin
declare #result datetime
select #result = create_date from sys.objects where name = #objname
return #result
end
go
I'd like to put this UDF in the master database or some other shared database so that it is accessible from anywhere, except that if I do that then the sys.objects reference pulls from the master database instead of the database that I'm initiating my query from. I know you can do this as the information_schema views sit in master and just wrap calls to local instances of sys.objects, so I'm hoping there's a simple way to do that with my UDF as well.
Try this:
CREATE FUNCTION dbo.GetObjCreateDate(#objName sysname, #dbName sysname)
RETURNS datetime AS
BEGIN
DECLARE #createDate datetime;
DECLARE #params nvarchar(50);
DECLARE #sql nvarchar(500);
SET #params = '#createDate datetime OUTPUT';
SELECT #sql = 'SELECT #createDate = create_date FROM ' + #dbName + '.sys.objects WHERE name = ''' + #objname + '''';
EXEC sp_executesql #sql, #params, #createDate = #createDate OUTPUT;
RETURN #createDate
END
;
Why not do this instead?
Create a stored procedure that creates a view in the master database containing all of the information in sys.objects from each database on the server.
Create a DDL Trigger that gets fired whenever a CREATE, ALTER or DROP statement is executed for a database. The trigger would then execute the stored procedure in step #1. This allows the view to be automatically updated.
(Optional) Create a user defined function that queries the view for the creation date of a given object.
Stored Procedure DDL:
USE [master];
GO
CREATE PROCEDURE dbo.BuildAllServerObjectsView
AS
SET NOCOUNT ON;
IF OBJECT_ID('master.dbo.AllServerObjects') IS NOT NULL
EXEC master..sp_SQLExec 'DROP VIEW dbo.AllServerObjects;';
IF OBJECT_ID('tempdb..Databases') IS NOT NULL
DROP TABLE #Databases;
DECLARE #CreateView varchar(8000);
SET #CreateView = 'CREATE VIEW dbo.AllServerObjects AS' + CHAR(13)+CHAR(10) + CHAR(13)+CHAR(10);
SELECT name COLLATE SQL_Latin1_General_CP1_CI_AS AS 'name'
INTO #Databases
FROM sys.databases
ORDER BY name;
DECLARE #DatabaseName nvarchar(100);
WHILE (SELECT COUNT(*) FROM #Databases) > 0
BEGIN
SET #DatabaseName = (SELECT TOP 1 name FROM #Databases ORDER BY name);
SET #CreateView +='SELECT N'+QUOTENAME(#DatabaseName, '''')+' AS ''database_name''' + CHAR(13)+CHAR(10)
+ ' ,name COLLATE SQL_Latin1_General_CP1_CI_AS AS ''object_name''' + CHAR(13)+CHAR(10)
+ ' ,object_id' + CHAR(13)+CHAR(10)
+ ' ,principal_id' + CHAR(13)+CHAR(10)
+ ' ,schema_id' + CHAR(13)+CHAR(10)
+ ' ,parent_object_id' + CHAR(13)+CHAR(10)
+ ' ,type' + CHAR(13)+CHAR(10)
+ ' ,type_desc' + CHAR(13)+CHAR(10)
+ ' ,create_date' + CHAR(13)+CHAR(10)
+ ' ,modify_date' + CHAR(13)+CHAR(10)
+ ' ,is_ms_shipped' + CHAR(13)+CHAR(10)
+ ' ,is_published' + CHAR(13)+CHAR(10)
+ ' ,is_schema_published' + CHAR(13)+CHAR(10)
+ ' FROM ' + QUOTENAME(#DatabaseName) + '.sys.objects';
IF (SELECT COUNT(*) FROM #Databases) > 1
SET #CreateView += CHAR(13)+CHAR(10) + CHAR(13)+CHAR(10) + ' UNION' + CHAR(13)+CHAR(10);
ELSE
SET #CreateView += ';';
DELETE #Databases
WHERE name = #DatabaseName;
END;
--PRINT #CreateView --<== Uncomment this to see the DDL for the view.
EXEC master..sp_SQLExec #CreateView;
IF OBJECT_ID('tempdb..Databases') IS NOT NULL
DROP TABLE #Databases;
GO
Function DDL:
USE [master];
GO
CREATE FUNCTION dbo.GetObjCreateDate(#DatabaseName sysname, #objName sysname) RETURNS DATETIME AS
BEGIN
DECLARE #result datetime;
SELECT #result = create_date
FROM master.dbo.AllServerObjects
WHERE [database_name] = #DatabaseName
AND [object_name] = #objname;
RETURN #result;
END
GO
Sample Usage:
SELECT master.dbo.GetObjCreateDate('MyDatabase', 'SomeObject') AS 'Created';
SELECT master.dbo.GetObjCreateDate(DB_NAME(), 'spt_monitor') AS 'Created';
Does it have to be a function? If you just want it accessible everywhere, a trick is to put your code in a varchar and sp_executesql it:
create procedure dbo.GetObjCreateDate(#objName sysname)
as
declare #sql nvarchar(max)
select #sql = 'select create_date from sys.objects where name = ''' + #objname + ''''
EXEC sp_executesql #sql
go
There seems to be an undocumented stored procedure that allows you to create your own system objects: sp_ms_marksystemobject
You can read more on http://www.mssqltips.com/tip.asp?tip=1612
Have a look at How to Write Your Own System Functions. I believe that it may help you

Resources