How to set table name in dynamic SQL query? - sql

I want to set table name in a dynamic SQL query. I tried successfully for parameter as following:
/* Using sp_executesql */
/* Build and Execute a Transact-SQL String with a single parameter
value Using sp_executesql Command */
/* Variable Declaration */
DECLARE #EmpID AS SMALLINT
DECLARE #SQLQuery AS NVARCHAR(500)
DECLARE #ParameterDefinition AS NVARCHAR(100)
/* set the parameter value */
SET #EmpID = 1001
/* Build Transact-SQL String by including the parameter */
SET #SQLQuery = 'SELECT * FROM tblEmployees WHERE EmployeeID = #EmpID'
/* Specify Parameter Format */
SET #ParameterDefinition = '#EmpID SMALLINT'
/* Execute Transact-SQL String */
EXECUTE sp_executesql #SQLQuery, #ParameterDefinition, #EmpID
Now I want to take TABLE NAME dynamically using a parameter but I've failed to do that. Please guide me.

To help guard against SQL injection, I normally try to use functions wherever possible. In this case, you could do:
...
SET #TableName = '<[db].><[schema].>tblEmployees'
SET #TableID = OBJECT_ID(TableName) --won't resolve if malformed/injected.
...
SET #SQLQuery = 'SELECT * FROM ' + QUOTENAME(OBJECT_NAME(#TableID)) + ' WHERE EmployeeID = #EmpID'

Table names cannot be supplied as parameters, so you'll have to construct the SQL string manually like this:
SET #SQLQuery = 'SELECT * FROM ' + #TableName + ' WHERE EmployeeID = #EmpID'
However, make sure that your application does not allow a user to directly enter the value of #TableName, as this would make your query susceptible to SQL injection. For one possible solution to this, see this answer.

Try this:
/* Variable Declaration */
DECLARE #EmpID AS SMALLINT
DECLARE #SQLQuery AS NVARCHAR(500)
DECLARE #ParameterDefinition AS NVARCHAR(100)
DECLARE #TableName AS NVARCHAR(100)
/* set the parameter value */
SET #EmpID = 1001
SET #TableName = 'tblEmployees'
/* Build Transact-SQL String by including the parameter */
SET #SQLQuery = 'SELECT * FROM ' + #TableName + ' WHERE EmployeeID = #EmpID'
/* Specify Parameter Format */
SET #ParameterDefinition = '#EmpID SMALLINT'
/* Execute Transact-SQL String */
EXECUTE sp_executesql #SQLQuery, #ParameterDefinition, #EmpID

This is the best way to get a schema dynamically and add it to the different tables within a database in order to get other information dynamically
select #sql = 'insert #tables SELECT ''[''+SCHEMA_NAME(schema_id)+''.''+name+'']'' AS SchemaTable FROM sys.tables'
exec (#sql)
of course #tables is a dynamic table in the stored procedure

Building on a previous answer by #user1172173 that addressed SQL Injection vulnerabilities, see below:
CREATE PROCEDURE [dbo].[spQ_SomeColumnByCustomerId](
#CustomerId int,
#SchemaName varchar(20),
#TableName nvarchar(200)) AS
SET Nocount ON
DECLARE #SQLQuery AS NVARCHAR(500)
DECLARE #ParameterDefinition AS NVARCHAR(100)
DECLARE #Table_ObjectId int;
DECLARE #Schema_ObjectId int;
DECLARE #Schema_Table_SecuredFromSqlInjection NVARCHAR(125)
SET #Table_ObjectId = OBJECT_ID(#TableName)
SET #Schema_ObjectId = SCHEMA_ID(#SchemaName)
SET #Schema_Table_SecuredFromSqlInjection = SCHEMA_NAME(#Schema_ObjectId) + '.' + OBJECT_NAME(#Table_ObjectId)
SET #SQLQuery = N'SELECT TOP 1 ' + #Schema_Table_SecuredFromSqlInjection + '.SomeColumn
FROM dbo.Customer
INNER JOIN ' + #Schema_Table_SecuredFromSqlInjection + '
ON dbo.Customer.Customerid = ' + #Schema_Table_SecuredFromSqlInjection + '.CustomerId
WHERE dbo.Customer.CustomerID = #CustomerIdParam
ORDER BY ' + #Schema_Table_SecuredFromSqlInjection + '.SomeColumn DESC'
SET #ParameterDefinition = N'#CustomerIdParam INT'
EXECUTE sp_executesql #SQLQuery, #ParameterDefinition, #CustomerIdParam = #CustomerId; RETURN

Related

Result set not permitted in BATCH STATEMENT - SQL Anywhere

I am new for SQL ANYWHERE. I am passing Table Name as a input parameter. I want to run both delete and select statement .I don't know where I missed my logic. Could you please help me to do this.
My procedure is
ALTER PROCEDURE "dba"."spCallTrigger"(
/* #parameter_name parameter_type [= default_value] [OUTPUT], ... */
#TableName varchar(25) )
AS
BEGIN
/* Type the procedure statements here */
//Exec "dba"."spCallTrigger" 'GTempTable'
SET OPTION ISQL_PRINT_RESULT_SET='ALL';
DECLARE #sql LONG VARCHAR
SET #sql = 'delete from dba.' + #TableName + ' where 1=2'
Execute ( #sql )
DECLARE #command LONG VARCHAR
SET #command = 'select * from dba.' + #TableName + 'Audit'
Execute ( #command )
END
EXECUTE IMMEDIATE WITH RESULT SET ON #command
This might work.

T-SQL Setting a scalar variable with the value of another scalar variable

Im creating a stored procedure that retrieves data to fill a radar chart. It worked pretty well using static tables und rows like this:
(This is just a piece of the code)
SELECT
#aAvg = CAST(AVG(1. * foerderpy_1617) as DECIMAL(18,4)),
#aMin = CAST(MIN(1. * foerderpy_1617) as DECIMAL(18,4)),
#aMax = CAST(MAX(1. * foerderpy_1617) as DECIMAL(18,4))
FROM foerderpy a WHERE SUBSTRING(a.BSN,3,1) = 'g';
But now i want a dynamic sql. I want the stored procedure to always take the latest row of my table:
(These are just pieces of the code)
DECLARE #SQL AS NVARCHAR(MAX);
DECLARE #aAvg AS NVARCHAR(MAX);
DECLARE #aMin AS NVARCHAR(MAX);
DECLARE #aMax AS NVARCHAR(MAX);
DECLARE #tabname SYSNAME;
DECLARE #coluname SYSNAME;
DECLARE #counter INTEGER;
SET #tabname = 'foerderpy'
SET #counter = (
SELECT MAX(ORDINAL_POSITION)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tabname
GROUP BY TABLE_NAME)
SET #coluname = (
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #tabname AND
ORDINAL_POSITION = #counter)
SET #aAvg = (SELECT CAST(AVG(1. * #coluname) as DECIMAL(18,4))FROM #tabname a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING(#restriction,3,1))
At the last line (the SET #aAvg), the stored procedure stops working and sql tells me "i have to declare #tabname", although i obv. declared it above. What is the problem im missing? Is it even possible to do what im trying?
The rest of the Code isn't causing any problems so i left it out. I need the #aAvg to calculate later in the procedure.
You need to run your last query using EXECUTE because EXECUTE:
Executes a command string or character string within a Transact-SQL batch
So you have to change the last line of your procedure in a way that the query is written in a string and called by execute.
DECLARE #sql VARCHAR(max) = 'SELECT CAST(AVG(1. * ' + #coluname + ') as DECIMAL(18,4))FROM '+ #tabname +' a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING('+#restriction+',3,1)';
EXECUTE(#sql);
If you would like to save the value in your variable #aAvg, you can use sp_executesql with an out parameter, this way:
DECLARE #sql VARCHAR(max) = 'SELECT CAST(AVG(1. * ' + #coluname + ') as DECIMAL(18,4))FROM '+ #tabname +' a WHERE SUBSTRING(a.BSN,3,1) = SUBSTRING('+#restriction+',3,1)';
exec sp_executesql #sql, N'#aAvg decimal(18,4) out', #aAvg out
select #aAvg

SQL Server Stored Procedure : Dynamic database in query

I have a variable which holds the database that I am working on. How can I add this variable in a static query?
This is what I want to achieve:
if exists(select * from #DestinationDB.[RaPa] where tid = #dyid)
begin
RAISERROR('Rapa exist',16,1)
end
I'm not sure if you meant without Dynamic SQL... but here is how you can accomplish this with dynamic SQL
declare #DestinationDB varchar(64)
declare #dyid int
declare #sql varchar(max)
set #DestinationDB = 'SomeDB'
set #dyid = 14
set #sql =
'if exists(select * from ' + quotename(#DestinationDB) + '.[RaPa] where tid = ' + cast(#dyid as varchar(16)) + ')
begin
RAISERROR(''Rapa exist'',16,1)
end'
print #sql
--exec(#sql)
Just uncomment the exec part when you are satisfied with the command.

How to return the rowcount of a dynamic sql query in SP?

I would like to return the rowcount of a dynamic sql query using linq similar to mentioned here:
http://weblogs.asp.net/scottgu/archive/2007/08/16/linq-to-sql-part-6-retrieving-data-using-stored-procedures.aspx
I'm using dynamic sql to create the where clause and to implement paging on the result set, The rowcount I want to return is the total number of records that meet the where condition.
My SQL that is causing me problems:
-- get row count
SET #SQL = '#TotalRowCount = SELECT COUNT(*) as TotalRowCount'
SET #SQL = #SQL + #WHERE
IF (LEN(#SUBWHERE) > 0)
BEGIN
SET #SQL = #SQL + #SUBWHERE
END
SET #SQL = #SQL + ')) '
exec sp_executesql #SQL
END
(I need this to be the output param #TotalRowCount in the param list here):
ALTER PROCEDURE [dbo].[_tournament_GetTournamentsByConveners]
(
#LastName varchar(100) = null ,
#Username varchar(256) = null ,
#Email varchar(100) = null ,
#IsWildcard bit = null,
#PageIndex int ,
#PageSize int,
#TotalRowCount int output
)
AS
This is by design.
The scope of the #TotalRowCount in the dynamic SQL is different to the scope of #TotalRowCount declared in the stored procedure. That is, the dynamic SQL has it's own scope.
If you insist on using dynamic SQL, do this to add the total rows to the record set that is returned
SELECT col1, col2,
COUNT(*) OVER () AS TotalRows
FROM ...
Otherwise we only have partial code to offer any suggestions for improvement. You appear to have LINQ to call stored procs with execute dynamic SQL. This is too convoluted.
You can declare output parameters with sp_executesql. So to get your result alter the code as shown below.
Always be careful when concatenating SQL code like this as it is extremely vulnerable to SQL injection.
DECLARE #SQL nvarchar(4000)
SET #SQL = N'SELECT #TotalRowCount = COUNT(*) as TotalRowCount'
SET #SQL = #SQL + #WHERE
IF (LEN(#SUBWHERE) > 0)
BEGIN
SET #SQL = #SQL + #SUBWHERE
END
SET #SQL = #SQL + N')) '
exec sp_executesql #SQL, N'#TotalRowCount int output', #TotalRowCount output
you can also express this more compact as:
DECLARE #SQL nvarchar(4000)
SET #SQL = N'SELECT #TotalRowCount = COUNT(*) as TotalRowCount' + #WHERE + ISNULL(#SUBWHERE, N'') + N'))'
exec sp_executesql #SQL, N'#TotalRowCount int output', #TotalRowCount output

How to secure dynamic SQL stored procedure?

I have a stored procedure that takes in the name of a table as a parameter and uses dynamic sql to perform the select. I tried to pass #TableName as a parameter and use sp_executesql but that threw an error. I decided to go with straight dynamic sql without using sp_executesql.
Is there anything else I should be doing to secure the #TableName parameter to avoid sql injection attacks?
Stored procedure below:
CREATE PROCEDURE dbo.SP_GetRecords
(
#TableName VARCHAR(128) = NULL
)
AS
BEGIN
/* Secure the #TableName Parameter */
SET #TableName = REPLACE(#TableName, ' ','')
SET #TableName = REPLACE(#TableName, ';','')
SET #TableName = REPLACE(#TableName, '''','')
DECLARE #query NVARCHAR(MAX)
/* Validation */
IF #TableName IS NULL
BEGIN
RETURN -1
END
SET #query = 'SELECT * FROM ' + #TableName
EXEC(#query)
END
This failed when using sp_executesql instead:
SET #query = 'SELECT * FROM #TableName'
EXEC sp_executesql #query, N'#TableName VARCHAR(128)', #TableName
ERROR: Must declare the table variable
"#TableName".
See here:
How should I pass a table name into a stored proc?
you of course can look at the sysobjects table and ensure that it exists
Select id from sysobjects where xType = 'U' and [name] = #TableName
Further (more complete example):
DECLARE #TableName nVarChar(255)
DECLARE #Query nVarChar(512)
SET #TableName = 'YourTable'
SET #Query = 'Select * from ' + #TableName
-- Check if #TableName is valid
IF NOT (Select id from sysobjects where xType = 'U' and [name] = #TableName) IS NULL
exec(#Query)