Update a null column in sql with spexecutesql - c#

I encountered a problem in the following query. When I go to update the column ID_Premesso with the value NULL the value does not change, while if I go to update with the value 0 it works.
SET #SQL = 'UPDATE ' + #NameTB + ' SET ID_Permesso = NULL WHERE ID_Timbratura = #IDTimbratura '
SET #PARAMS = '#IDTimbratura INT'
EXECUTE sp_executesql #SQL, #PARAMS, #IDTimbratura;

= does not work for NULL values. So, if ID_Timbratura can be NULL, you need to take that into account:
SET #SQL = '
UPDATE ' + #NameTB + '
SET ID_Permesso = NULL
WHERE ID_Timbratura = #IDTimbratura OR
(ID_Timbratura IS NULL AND #IDTimbratura IS NULL)' ;
SET #PARAMS = N'#IDTimbratura INT';
EXECUTE sp_executesql #SQL, #PARAMS, #IDTimbratura;
Note that SQL Server lets you break strings over multiple lines. I reformatted the query string so it is easier to read.

Related

How to pass in a string into dynamic sql inside a stored procedure

As of now I have a SQL statement like this:
SELECT
y.ID, y.STATUS, y.CONTROLID, y.TRCDE
FROM
YTDTRNI AS y
LEFT JOIN
VND AS v ON y.COMNO = v.COMNO
WHERE
TRCDE ='RC'
ORDER BY
ID DESC
I am trying to use this query inside a stored procedure using dynamic SQL. So far, my stored procedure looks like this:
SET #query = N'SELECT y.ID, y.STATUS, y.CONTROLID, y.TRCDE
FROM YTDTRNI AS y LEFT JOIN VND AS v ON y.COMNO = v.COMNO
WHERE TRCDE = ' + #searchtrtype + '
ORDER BY ' + #orderbycondition
The variables #searchtrtype and #orderbycondition are of type nvarchar.
I am using a ASP.NET/C# program to call the stored procedure. However, it breaks with an exception:
An expression of non boolean type in a context where a condition is expected near 'ORDER'
I think I am getting error because the string values are not properly concatenated or formatted in inside the #query variable.
Open to any advice.
EDIT: my stored procedure looks like this at the moment:
When I execute the stored procedure, it returns the result set that I want but it also shows an error message :
Must declare the scalar variable "#dsearchtrtype".
I tried declaring it inside the BEGIN body and declaring it as part of a parameter for the stored procedure but it still shows that same message.
Learn to use parameters with sp_executesql. You can do this for #searchtrtype, but not #orderbycondition:
SET #query = N'
SELECT y.ID, y.STATUS,y.CONTROLID,y.TRCDE
FROM YTDTRNI y LEFT JOIN
VND v
ON y.COMNO = v.COMNO
WHERE TRCDE = #searchtrtype
ORDER BY #orderbycondition';
SET #query = REPLACE(#query, '#orderbycondition', #orderbycondition);
EXEC sp_executesql #query,
N'#searchtrtype NVARCHAR(255)', -- or whatever the right type is
#searchtrtype=#searchtrtype;
You cannot pass in identifiers, only values, so this doesn't work for #orderbycondition.
You need to parametrise your SQL. Concatenating string for SQL is an awful idea; as it leaves you open to injection. The way you want to do it would be:
DECLARE #query nvarchar(MAX);
SET #query = N'SELECT y.ID,' + NCHAR(10) +
N' y.STATUS,' + NCHAR(10) +
N' y.CONTROLID,' + NCHAR(10) +
N' y.TRCDE' + NCHAR(10) +
N'FROM YTDTRNI AS y' + NCHAR(10) +
N' LEFT JOIN VND AS v ON y.COMNO = v.COMNO ' + NCHAR(10) +
N'WHERE TRCDE = #dsearchtrtype' + NCHAR(10) +
N'ORDER BY ' + QUOTENAME(#orderbycondition) + N';';
PRINT #SQL;
EXEC sp_executesql #query, N'#dsearchtrtype nvarchar(100)', #dsearchtrtype = #searchtrtype;
This, however, probably isn't going to work, due to your variable #orderbycondition. I don't know, however, what type of value that has, however, if it's something like 'ID desc', this it would become ORDER BY [ID desc];.
If this assumption is correct, I would suggest using 2 variables; one for the sort column and one for the direction, and replace the final line of the dynamic SQl with:
N'ORDER BY ' + QUOTENAME(#orderbycolumn) + N' ' + CASE WHEN #orderbydirection NOT IN(N'ASC',N'DESC') THEN N'' ELSE #orderbydirection END + N';';
If your value of #orderbycondition can be more complex than that, post a comment to let me know (and update your question with more detail), and I'll be happy to explain how to create a more dynamic ORDER BY clause using a table type parameter, along with adding it to your dynamic SQL with use of STUFF and FOR XML PATH.
Based on the example provided it appears that the OP simply wants to filter on a single column and perform a sort on a user specified column. This can be achieved with a parametrized query without the need of dynamic SQL.
Within the where clause the TRCDE field can be directly assigned to the #searchtrtype parameter. The dynamic sorting requirement is a little trickier but can be achieved by using a CASE statement within the ORDER BY clause. An additional parameter will have to be included to indicate if the sorting should be ascending or descending. The following query demonstrates:
DECLARE #orderByColumn NVARCHAR(50) = 'ID'; -- The column to sort by
DECLARE #sortAscending BIT = 1; -- Indicate sorting order 1 == Ascending, 0 == Descending
DECLARE #searchtrtype NVARCHAR(10) = 'RC'
IF #sortAscending = 1 -- Sort data in ascending order
BEGIN
SELECT y.[ID]
,y.[STATUS]
,y.[CONTROLID]
,y.[TRCDE]
FROM YTDTRNI AS y
LEFT JOIN VND AS v ON y.[COMNO] = v.[COMNO]
WHERE TRCDE = #searchtrtype
ORDER BY (
-- Create case statement for every column user may want to filter on
CASE
WHEN #orderByColumn = 'ID' THEN [ID]
ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
END
),
(
CASE
WHEN #orderByColumn = 'STATUS' THEN [STATUS]
ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
END
),
(
CASE
WHEN #orderByColumn = 'CONTROLID' THEN [CONTROLID]
ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
END
) ASC;
END
ELSE
BEGIN
SELECT y.[ID]
,y.[STATUS]
,y.[CONTROLID]
,y.[TRCDE]
FROM YTDTRNI AS y
LEFT JOIN VND AS v ON y.[COMNO] = v.[COMNO]
WHERE TRCDE = #searchtrtype
ORDER BY (
CASE
WHEN #orderByColumn = 'ID' THEN [ID]
ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
END
),
(
CASE
WHEN #orderByColumn = 'STATUS' THEN [STATUS]
ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
END
),
(
CASE
WHEN #orderByColumn = 'CONTROLID' THEN [CONTROLID]
ELSE NULL -- Simply return null if we are not ordering by this column, effectively ignore the column.
END
) DESC;
END
This method is more work but will be easier to troubleshoot, maintain and will protect you from SQL Injection.
do you just need to include escaped apostrophes. if #searchtrtype can contain apostrophes, you might have to escape them with REPLACE(#searchtrtype, '''', '''''')
SET #query = N'SELECT y.ID, y.STATUS,y.CONTROLID,y.TRCDE
FROM YTDTRNI AS y LEFT JOIN VND AS v ON y.COMNO = v.COMNO
WHERE TRCDE = ''' + #searchtrtype + '''
ORDER BY ' + #orderbycondition

Unable to get Dynamic SQL to pass a dynamic table parameter

Not worried about SQL Injection or anything of the like, just trying to get this to work. Using SSMS and Visual Studio.
I have C# code that passes a variable, GlobalVariables.username, to an SQL parameter.
private void btnNext_Click(object sender, EventArgs e)
{
if (checkIntrotoPublicSpeaking.Checked || checkEffectiveOralCommunication.Checked || checkProfComm.Checked)
{
List<SqlParameter> sqlOralComm = new List<SqlParameter>();
sqlOralComm.Add(new SqlParameter("Username", GlobalVariables.username));
sqlOralComm.Add(new SqlParameter("IntrotoPublicSpeaking", cboxIntrotoPublicSpeaking.Text));
sqlOralComm.Add(new SqlParameter("EffectiveOralCommunication", cboxEffectiveOralCommunication.Text));
sqlOralComm.Add(new SqlParameter("ProfComm", cboxProfComm.Text));
DAL.ExecSP("CreateOralComm", sqlOralComm);
}
}
I've been reading into Dynamic SQL and saw that to pass the table name as a parameter, you have to construct it manually and execute it as "SET..." etc, etc. I've been trying slightly different modifications of the last 3 lines below. Each time, I'm greeted with an "invalid syntax near ..." exception pertaining to different parts of that line. In stack exchange it's broken into 3 lines but in SSMS it's one line, a little easier to read.
Status is nvarchar column and Course is an int column.
ALTER PROCEDURE [dbo].[CreateOralComm]
-- Add the parameters for the stored procedure here
#Username nvarchar(30),
#IntrotoPublicSpeaking nvarchar(3),
#EffectiveOralCommunication nvarchar(3),
#ProfComm nvarchar(3)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE #sql as nvarchar(max)
SET #sql = 'UPDATE ' + #Username + ' SET Grade = ' +
#IntrotoPublicSpeaking + ' Status = "Completed" WHERE Course = 7600105';
EXEC sp_executesql #sql;
END
GO
I know that global variable works, I have another line of code that's just a MessageBox displaying the value and it's correct. Just can't get those last few lines of SQL to work. I'm trying out just this first part, #IntrotoPublicSpeaking, before I move onto the other 2.
Any help would be really appreciated.
Two things here:
DECLARE #sql as nvarchar(max)
SET #sql = 'UPDATE ' + #Username + ' SET Grade = ' +
#IntrotoPublicSpeaking + ' Status = "Completed" WHERE Course = 7600105';
EXEC sp_executesql #sql;
Missing comma before Status and I think you do need to use single quotes
DECLARE #sql as nvarchar(max)
SET #sql = 'UPDATE ' + #Username + ' SET Grade = ' +
#IntrotoPublicSpeaking + ', Status = ''Completed'' WHERE Course = 7600105';
EXEC sp_executesql #sql;

prevent sql injection in proc in linq c#

I have a search form with 3 textboxes (stud_name, stud_city, stud_state). When I enter this vaue ' drop table users in the name textbox, it will drop the table successfully. Here is my c# code for calling stored procedure. How to prevent this type of injection using linq in c#
using (iDataContext db = new iDataContext(connectionString))
{
var statusList = db.SP_Student_LOOKUP(name, city, state);
return statusList.ToList<SP_Student_LOOKUPResult>();
}
here is my sp
CREATE proc SP_Student_LOOKUP #name varchar(50),
#city varchar(50),
#state varchar(2)
as
declare #sql varchar(2048)
begin
set #sql = 'select * from student where ';
if (((#name is not null)) and (len(#name) > 0))
set #sql = #sql + ' studentname like ''%'+#name+'%'' and '
if (((#city is not null)) and (len(#city) > 0))
set #sql = #sql + ' studentcity like ''%'+#city+'%'' and '
if (((#state is not null)) and (len(#state) > 0))
set #sql = #sql + ' studentstate like ''%'+#state+'%'' and '
print #sql
exec( #sql )
Your stored proc is susceptible to sql injection because you're (needlessly) using dynamic sql to build up your query. Don't do this.
Your stored procedure should be
CREATE proc SP_Student_LOOKUP
#name varchar(50),
#city varchar(50),
#state varchar(2)
as
select * from student
where
(#name is null or len(#name) = 0 or studentname LIKE #name)
and (#city is null or len(#city) = 0 or studentcity like #city)
and (#state is null or len(#state) = 0 or studentstate like #state)
This has the same behaviour as your dynamic sql, with 1 caveat - to fully stop the sql injection vulnerability you need to wrap % and % around your inputs - by doing this inside the stored proc you will re-introoduce the same problem
If you're using Entity Framework, as I'm guessing, you shouldn't be using stored procedures to retrieve data, unless your query is really beyond the capabilities of LINQ (and it isn't). You should build your query in LINQ, like that:
using (iDataContext db = new iDataContext(connectionString))
{
var statusList = from s in db.Set<student>()
select s;
if (!String.IsNullOrEmpty(name))
{
statusList = statusList.Where(s => s.studentname.Contains(name));
}
if (!String.IsNullOrEmpty(city))
{
studentList = studentList.Where(s => s.studentcity.Contains(city));
}
if (!String.IsNullOrEmpty(state))
{
studentList = studentList.Where(s => s.studentstate.Contains(state));
}
return statusList.ToList();
}
and if you're really using the old DataContext object, please read a more recent EF tutorial. This has been superceeded by DbContext (where you will find the .Set<>() method I used).
I agree with #TsahiAsher and #Jamiec
but clearly you are adamant to stick to the dynamic sql,
Given the constraints, I'd suggest that you parameterize your dynamic sql and execute it with the parameters you got.
(note i've also changed the position of "And" statements because if all 3 parameters are empty, you'd get an incomplete sql statement)
db fiddle Here
-- If I recall correct, it has to be NVARCHAR
declare #sql nvarchar(2048)
set #sql = 'select * from student where 1=1 ';
if ((#name is not null) and (len(#name) > 0))
set #sql = #sql + 'AND studentname like ''%''+#name+''%'' '
if ((#city is not null) and (len(#city) > 0))
set #sql = #sql + 'AND studentcity like ''%''+#city+''%'' '
if ((#state is not null) and (len(#state) > 0))
set #sql = #sql + 'AND studentstate like ''%''+#state+''%'' '
print (#sql)
exec sp_executesql #sql, N'#name VARCHAR(50),#city VARCHAR(50),#state VARCHAR(2)', #name = #name, #city = #city, #state=#state
Sanitize the input strings by escaping single quotes within it:
using (iDataContext db = new iDataContext(connectionString))
{
var statusList = db.SP_Student_LOOKUP(name.Replace("'", #"\'"), city.Replace("'", #"\'"), state.Replace("'", #"\'")
return statusList.ToList<SP_Student_LOOKUPResult>();
}

SQL execute string issue with National language character?

i have a simple sp in sql as bellow :
alter proc sptest(#val nvarchar(30))
as
select COUNT(*) from AAtable
where name = #val
as i call this sp with #val = 'مریم', it works well and returns value.
but when i change it like this :
alter proc sptest(#val nvarchar(30))
as
declare #q nvarchar(max)
set #q = 'select COUNT(*) from AAtable where name = ' + #val
Execute(#q)
and call it with the same #val, it converts #val value to "?" and returns error. i should say that #val comes from a string parameter in c#. as i know .net convert string to nvarchar parameter form sql. anyway i can not add N before #val.
and also i HAVE to make a string query and execute it so i can not change it to the first code too.
if i do this : set #q = 'select COUNT(*) from AAtable where name = N''' + #val + '''' it does not work too.
why execute string makes my nvarchar parameter to "?" !?
Add the N in front of your text, so it is an NVARCHAR literal, not a VARCHAR, like this: N'My Text'
Try marking the first string of your concatenation operation as explicit Unicode:
set #q = N'select COUNT(*) from AAtable where name = ' + #val
This should keep your #val in Unicode as well.
ALTER proc sptest(#val nvarchar(30))
AS
DECLARE #retval INT
DECLARE #SQLString nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
SET #SQLString =
N'select #retvalOUT = COUNT(*) from AAtable where name = #Name';
SET #ParmDefinition = N'#Name varchar(30), retvalOUT int OUTPUT';
EXECUTE sp_executesql #SQLString, #ParmDefinition,
#Name = #val,
#retvalOUT=#retval OUTPUT;
SELECT #retval;

How do I get the correct sql quoted string for a .NET DbType

I want to run an ALTER TABLE that adds a default value constraint to a column.
I generate this statement dynamically from a .NET program.
How do I best format and quote the value when building my sql - now that ALTER TABLE statements does not support parameters (Gives the error 'Variables are not allowed in the ALTER TABLE statement').
Is there a utility for that in .NET? Or another solution?
You can do this in TSQL; for example, say you parameterize the command, passing in #DefaultValue, a varchar which may or may not be a valid TSQL literal. Because we are writing DDL, we will need to concatenate and exec, however we clearly don't want to blindly concatenate, as the value could be illegal. Fortunately, quotename does everything we need. By default, quotename outputs [qualified object names], but you can tell it to operate in literal escaping mode, for both single-quote and double-quote literals.
So our query that accepts #DefaultValue can build an SQL string:
declare #sql nvarchar(4000) = 'alter table ...';
-- ... blah
-- append the default value; note the result includes the outer quotes
#sql = #sql + quotename(#DefaultValue, '''');
-- ... blah
exec (#sql);
Full example:
--drop table FunkyDefaultExample
create table FunkyDefaultExample (id int not null)
declare #tableName varchar(20) = 'FunkyDefaultExample',
#colName varchar(20) = 'col name',
#defaultValue varchar(80) = 'test '' with quote';
-- the TSQL we want to generate to exec
/*
alter table [FunkyDefaultExample] add [col name] varchar(50) null
constraint [col name default] default 'test '' with quote';
*/
declare #sql nvarchar(4000) = 'alter table ' + quotename(#tablename)
+ ' add ' + quotename(#colName) + 'varchar(50) null constraint '
+ quotename(#colName + ' default') + ' default '
+ quotename(#defaultValue, '''');
exec (#sql);
-- tada!
string.Format("alter table YourTable add constraint DF_YourTable_Col1 default '{0}'",
inputValue.Replace("'", "''"));

Categories

Resources