sp_executesql with OUTPUT Parameter on SET statement - sql-injection

I want to use sp_executesql to set #email_body for sp_send_dbmail
Why?
Previously I was concatenating parameter directly to the #email_body which is prone to SQL Injection.
like this
SET #email_body = 'some html' + #id + 'some html' + #name + 'some html';
SQL injection is possible as we don't have control over input.
What have I done?
so above was just an example
my query looks closely like this
DECLARE #sql NVARCHAR(MAX);
DECLARE #email_body VARCHAR(max);
DECLARE #ParamDef NVARCHAR(MAX);
DECLARE #id INT;
DECLARE #name NVARCHAR(MAX);
DECLARE #status NVARCHAR(MAX);
-- select statement to input have input in #id, #name and #status from sometable;
SET #sql = N'SET #email_bodyOUT = CASE #status
WHEN ''A'' THEN ''SOME HTML'' + #id + ''SOME HTML'' + #name + ''SOME HTML''
WHEN ''B'' THEN ''SOME HTML'' + #id + ''SOME HTML'' + #name + ''SOME HTML''
WHEN ''C'' THEN ''SOME HTML'' + #id + ''SOME HTML'' + #name + ''SOME HTML'' END';
SET #ParamDef = N'#email_bodyOUT VARCHAR(MAX) OUTPUT
, #status NVARCHAR(MAX)
, #id INT
, #name NVARCAHR(MAX)';
EXECUTE sp_executesql #sql
, #ParamDef
, #email_bodyOUT = #email_body OUTPUT
, #status = #status
, #id = #id
, #name = #name;
SELECT #email_bodyOUT;
--executing sp_send_dbmail and send email
But nothing gets attached in #email_body and mail comes empty.
It works fine when I run simple query like this with sp_executesql
DECLARE #First_Name NVARCHAR(200) = 'abc';
EXEC sp_executesql N'
declare #HTML nvarchar(max) = ''Hi'' + #FN + '' , Rest of HTML email :) :) ''
SELECT #HTML',
N'#FN VARCHAR(8000)',
#First_Name;
Thanks in advance for your help :)

Found The problem, It was caused as one of the parameter was Null, which caused #emailbody to be null. Fixed it by ISNULL function.

Related

how to put exec(#string) into set #var in tsql?

so I was trying to create a index fragmentation stored procedure is TSQL but I am encoutering an error and can not wrap my head around this problem
the code i used is:
create or alter proc sp_maintain_index (#db nvarchar(50), #table nvarchar(50),
#index nvarchar(50)) as
begin
declare #ext_frg float
declare #int_frg float
declare #string1 nvarchar(500)
declare #string2 nvarchar(500)
set #string1 = N'select avg(avg_fragmentation_in_percent)
from sys.dm_db_index_physical_stats
(DB_ID(''' + #db + N'''), object_id(''' + #table + N'''),
Object_ID(''' + #index + N'''), null, ''Detailed'') group by object_id'
exec sp_executesql #string1, N'#ext_frg float out', #ext_frg OUTPUT
set #string2 = N'select avg(avg_page_space_used_in_percent)
from sys.dm_db_index_physical_stats
(DB_ID(''' + #db + N'''), object_id(''' + #table + N'''),
Object_ID(''' + #index + N'''), null, ''Detailed'') group by object_id'
exec sp_executesql #string2, N'#int_frg float out', #int_frg OUTPUT
declare #string nvarchar(500)
set #string = N'alter index ' + #index + N' on ' + #table
begin
if (#ext_frg >= 5 and #ext_frg <=30) or ((100 - #int_frg) < 70)
set #string = #string + N' reogranize'
else if (#ext_frg > 30) or ((100 - #int_frg) < 30)
set #string = #string + N' rebuild'
end
execute(#string)
end
go
exec dbo.sp_maintain_index N'virtual29', N'dbo.person', N'randomid'
Works now. Thanks for the flags. I messed up the dynamic sql, and used nvarchar instead for the variables.

result of sp_executesql in a variable

How can I get the output of the below query in a variable without temp table?
DECLARE #Q1 NVARCHAR(300)
DECLARE #Q2 NVARCHAR(300)
DECLARE #Q3 NVARCHAR(300)
SET #Q1 = 'SELECT ' +' ' + #formfieldpath
SET #Q2 = 'FROM [TestDatabase].[details] WHERE id ' + '=''' + CAST(#id AS VARCHAR(10)) + '''';
SET #Q3 = #Q1 +' '+ #Q2
PRINT #Q3
EXEC sp_executesql #Q3
Tried 'How to get sp_executesql result into a variable?' and not able to get the results.
Assuming that you get a singleton value from your dynamic statement:
DECLARE #ID int, --Is set somewhere
#YourVariable nvarchar(30), --Use an appropriate data type
#formfieldpath sysname; --Is set somewhere
DECLARE #SQL nvarchar(MAX);
--I assume that the name [TestDatabase].[details] is wrong, as [TestDatabase] would be the name of the schema, not the database,
--and I ASSUME you haven't foolishy created a schema called "TestDatabase"
SET #SQL = N'SELECT #YourVariable = ' + QUOTENAME(#formfieldpath) + N' FROM dbo.Details WHERE id = #id';
--Use the correct datatype for #YourVariable
EXEC sys.sp_executesql #SQL, N'#id int, #YourVariable nvarchar(30) OUTPUT', #id, #YourVariable OUTPUT;
Never inject unsanitised values into a dynamic SQL statement. SQL injection is a huge problem that should have stopped existing over a decade ago. Dos and Don'ts of Dynamic SQL

SQL Server Exception handling in a cursor defined query

Here is the script I am using to run against all the databases on sql server but somehow the script stops when the database is in transition so all I want to do is the script needs to skip the database that is in transition and proceed with rest of the databases. Any help would be appreciated.
declare #name varchar(max)
SET #starttime = GETDATE()
Declare DB_cursor cursor for
SELECT Name
FROM sys.databases
WHERE name NOT IN ('master', 'model', 'msdb', 'tempdb')
AND is_read_only = 0
AND is_in_standby = 0
AND state = 0
OPEN DB_cursor
FETCH NEXT FROM DB_cursor INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC(' use [' + #name + '] IF EXISTS (select name from sys.objects where name=''#Tablename'') UPDATE STATISTICS [' + #SchemaName + '].[' + #TableName +'](['+#StatsName+']) WITH FULLSCAN ')
FETCH NEXT FROM DB_cursor INTO #name
END
CLOSE DB_cursor
DEALLOCATE DB_cursor
Try:
DECLARE #sql NVARCHAR(MAX)
SET #sql = ' use [' + #name + '] IF EXISTS (select name from sys.objects where name=''#Tablename'') UPDATE STATISTICS [' + #SchemaName + '].[' + #TableName +'](['+#StatsName+']) WITH FULLSCAN ' )
EXEC sp_executesql #sql = #sql

How do i can resolve this promblem?

I have an table include 1 column ID and 12 columns month (Month01 to Month12). If i have an parameter #month=9, how can i compare to get column Month01 to Month09 without Month10, Month11, Month12. Please help me!
Simo's answer is more elegant, but below is an alternative "basic" way to do this:
DECLARE #month INT
SET #month = 9
IF #Month = 1
BEGIN
SELECT Id, Month01
FROM MonthlyData
END
IF #Month = 2
BEGIN
SELECT Id, Month01, Month02
FROM MonthlyData
END
-- Repeat for months 3-9
IF #Month = 9
BEGIN
SELECT Id, Month01, Month02, Month03, Month04,
Month05, Month06, Month07, Month08, Month09
FROM MonthlyData
END
-- Repeat for months 10-12
See my SQL Fiddle.
declare #str varchar(1000)
SELECT #str= coalesce(#str + ', ', '') + a.name
FROM (
Select name from sys.columns
where object_id = OBJECT_ID('table_months')
and isnumeric(right(name,2))= 1 and name like 'month%'
and cast(right(name,2) as int) < '03') a
-- Instead of '03' you use a variable and assign required moths number
declare #sql nvarchar(100)
set #sql = 'select ID, '+ #str+' from table_months'
exec sp_executesql #sql
you may try this out also :)
DECLARE #month int = 9
DECLARE #Sql nvarchar(100) =''
DECLARE #pos int = 1
WHILE(#pos <= #month)
BEGIN
SET #Sql = #Sql + (',month' + right('00'+ rtrim(#pos), 2))
SET #pos = #pos + 1
END
SET #Sql= 'SELECT ID ' +#Sql + ' FROM table'
EXEC sp_executesql #Sql
DECLARE #month int
DECLARE #tableName nvarchar(128)
SET #month = 9;
SET #tableName = 'months' -- YOUR TABLE's NAME
DECLARE #query nvarchar(2048)
SET #query =
'SELECT id, ' + STUFF((SELECT DISTINCT ', ' + name
FROM sys.columns WHERE name LIKE'Month%'
AND object_id = OBJECT_ID(#tableName)
AND CONVERT(INT, RIGHT(name, 2)) <= #month
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
+ ' FROM ' + #tableName;
EXEC(#query)

Adding GUID parameter to a sql string

I am having complications adding a guid type to a sql string and getting the query to go. I have to write the sql this because I need to pass the name database as a parameter.
The following code is what I'm trying to do but it doesnt work. No matter what I do it doesn't read the GUID correctly. It gives me the following error when executing the stored proc. Probably because guid has "-" in them
Incorrect syntax near '-'.
Here's the code:
ALTER PROCEDURE [dbo].[templatesAndBaskets_Select]
#guid uniqueidentifier,
#DatabaseName varchar(50)
AS
BEGIN
Declare #sql varchar(max)
Set #sql=
'select soldtoid
from ' + #DatabaseName + '.templatesAndBaskets' +
' where ordergroupid = ' + CONVERT(varchar(max),#guid)
print #sql
EXECUTE SP_EXECUTESQL #sql
END
You probably just simply need to put single quotes around the GUID :
SET #sql =
N'SELECT soldtoid FROM ' + #DatabaseName + N'.templatesAndBaskets' +
N' WHERE ordergroupid = ''' + CONVERT(nvarchar(max), #guid) + N''''
That should result in a SQL statement something like this:
SELECT soldtoid FROM database.templatesAndBaskets
WHERE ordergroupid = '5E736CE7-5527-40ED-8499-2CA93FC7BC9C'
which is valid - yours without the single quotes around the GUID isn't valid...
Update: you also need to change your #sql variable to NVARCHAR for sp_Executesql - try this:
ALTER PROCEDURE [dbo].[templatesAndBaskets_Select]
#guid uniqueidentifier,
#DatabaseName NVARCHAR(50)
AS
BEGIN
DECLARE #sql NVARCHAR(MAX)
SET #sql =
N'SELECT soldtoid FROM ' + #DatabaseName + N'.templatesAndBaskets' +
N' WHERE ordergroupid = ''' + CONVERT(nvarchar(max), #guid) + N''''
PRINT #sql
EXECUTE sp_ExecuteSQL #sql
END
Does that work??
Put the #guid between '
ALTER PROCEDURE [dbo].[templatesAndBaskets_Select]
#guid uniqueidentifier,
#DatabaseName varchar(50)
AS
BEGIN
Declare #sql varchar(max)
Set #sql=
"select soldtoid from " + #DatabaseName + ".templatesAndBaskets" +
" where ordergroupid = '" + CONVERT(varchar(max),#guid)+"'"
print #sql
EXECUTE SP_EXECUTESQL #sql
END