SQL Dynamic Query String Breaks When Variable Contains Single Quote - sql

I have a SQL query string that is like this:
DECLARE #sql varchar(max)
SET #sql = ' INSERT INTO ' + #tempTable1 +
' SELECT 0 as Type1, 0 as Type2, ' +
'''' + #name + ''' as CompanyName ' +
' FROM #tempTable2 tt2'
The query runs fine except for two names that happen to contain a single quote (ex: Pete's Corner). When either one of these names becomes part of the query it breaks the query string. I thought the easiest thing to do would be to replace the single quote like this replace(#name,'''','') but it doesn't work because I'm already in a string and so its affecting the rest of the statement. Altering the table itself is not an option unfortunately.
How can I replace or remove these single quotes?
Addition: I apologize, I did not include the part where #name is actually being populated from another database table by a join so setting the value of #name before the string is created I think would be difficult for me.

Why do you need to do this at all? You should be passing strong parameters to sp_executesql instead of munging all of your parameters into a single string and using EXEC(). More info on that here.
DECLARE #sql NVARCHAR(MAX), #name NVARCHAR(32);
SET #name = 'Pete''s Corner';
SET #sql = 'INSERT INTO ' + #tempTable1 +
' SELECT 0 as Type1, 0 as Type2, #name as CompanyName ' +
' FROM #tempTable2 tt2';
EXEC sp_executesql #sql, N'#name NVARCHAR(32)', #name;
I presume the #name parameter actually gets populated from elsewhere, and if using proper parameterization you shouldn't have to deal with escaping the '.
Now I'm not quite sure what #tempTable1 is supposed to represent, or if you can access #tempTable2 from this scope, but whenever you find yourself running a replace that requires '''' or '''''' (or both), you should ask yourself if maybe there's a better way.

I think this should do it:
DECLARE #sql varchar(max)
SET #sql = ' INSERT INTO ' + #tempTable1 +
' SELECT 0 as Type1, 0 as Type2, ' + ''''+
replace( #name ,'''','''''')+''''+' as CompanyName
FROM #tempTable2 tt2'

You can use sp_executesql system procedure. sp_executesql will allow you to call dynamic SQL with #name parameter instead of embedding it into the SQL.
DECLARE #sql nvarchar(max),
#name varchar(50)
SET #name = 'qwe'''
SET #sql = 'INSERT INTO ' + #tempTable1 +
' SELECT 0 as Type1, 0 as Type2, ' +
'#name as CompanyName ' +
'FROM #tempTable2 tt2'
--PRINT #sql
EXEC sp_executesql #sql, N'#name varchar(50)', #name

Related

How to use a variable in group by clause in dynamic SQL?

I need to have a dynamic SQL statement which should do aggregations on the columns which will pass to it as variables. But I got the below error.
The pseudo code is as follow:
DECLARE #sql nvarchar(max)
DECLARE #params nvarchar(max)
SET #sql = N'SELECT #Input1, #Input2, COUNT(ID) FROM Customers GROUP BY #Input1, #Input2'
SET #params = N'#Input1 nvarchar(225), #Input2 nvarchar(255)'
sp_executesql #sql, #params, #Input1 'Name', #Input2 'Age'
And the error is as bellow:
"Each GROUP BY expression must contain at least one column that is not
an outer reference."
I don't really get the cause of this error, so any help is appreciated in advance.
You have a couple of options, sqlcmd allows substitution for things like column names, or you need to build up the query dynamically. Ensure you check the inputs to avoid sql injection.
SET #sql = N'SELECT ' + #Input1 + ', ' + #Input2 + ', COUNT(ID) FROM Customers GROUP BY ' + #Input1 + ', ' + #Input2;

Dynamic SQL Query: Replace all single quotes to double quote

I have a dynamic SQL query and some params are being passed in such as this
Set #SQL = 'Update Table1 Set Status = 1 Where ' +
'Name = ''' + Cast(#Name As Varchar(50)) + '''' +
' Product = ''' + Cast(#Product As Varchar(50)) + ''''
I have issues with the single quote being passed in the param. If the #Name and #Product has no single quote at all, I won't have any issue. But sometimes the #Name or #Product would have single quote and that causes failure to execute my dynamic query as it's not properly formatted because of the additional single quote.
I have lots more param other than #Name and #Product, and I don't want to do something like REPLACE(#Name, '''','''''') for every single param unless it's necessary.
Is there a way that I can replace every single quote to double in my dynamic query before execution or better alternative?
Alternative but more hard work here:
Set #SQL = 'Update Table1 Set Status = 1 Where ' +
'Name = Cast(#Name As NVarchar(50)) ' +
' Product = Cast(#Product As NVarchar(50)) '
EXEC sp_executesql #SQL , N'#Name NVARCHAR(50), #Product NVARCHAR(50)', #Name, #Product
Let me know, if this worked for you ?

Can I pass column name as input parameter in SQL stored Procedure

create procedure sp_First
#columnname varchar
AS
begin
select #columnname from Table_1
end
exec sp_First 'sname'
My requirement is to pass column names as input parameters.
I tried like that but it gave wrong output.
So Help me
You can do this in a couple of ways.
One, is to build up the query yourself and execute it.
SET #sql = 'SELECT ' + #columnName + ' FROM yourTable'
sp_executesql #sql
If you opt for that method, be very certain to santise your input. Even if you know your application will only give 'real' column names, what if some-one finds a crack in your security and is able to execute the SP directly? Then they can execute just about anything they like. With dynamic SQL, always, always, validate the parameters.
Alternatively, you can write a CASE statement...
SELECT
CASE #columnName
WHEN 'Col1' THEN Col1
WHEN 'Col2' THEN Col2
ELSE NULL
END as selectedColumn
FROM
yourTable
This is a bit more long winded, but a whole lot more secure.
No. That would just select the parameter value. You would need to use dynamic sql.
In your procedure you would have the following:
DECLARE #sql nvarchar(max) = 'SELECT ' + #columnname + ' FROM Table_1';
exec sp_executesql #sql, N''
Try using dynamic SQL:
create procedure sp_First #columnname varchar
AS
begin
declare #sql nvarchar(4000);
set #sql='select ['+#columnname+'] from Table_1';
exec sp_executesql #sql
end
go
exec sp_First 'sname'
go
This is not possible. Either use dynamic SQL (dangerous) or a gigantic case expression (slow).
Create PROCEDURE USP_S_NameAvilability
(#Value VARCHAR(50)=null,
#TableName VARCHAR(50)=null,
#ColumnName VARCHAR(50)=null)
AS
BEGIN
DECLARE #cmd AS NVARCHAR(max)
SET #Value = ''''+#Value+ ''''
SET #cmd = N'SELECT * FROM ' + #TableName + ' WHERE ' + #ColumnName + ' = ' + #Value
EXEC(#cmd)
END
As i have tried one the answer, it is getting executed successfully but while running its not giving correct output, the above works well
You can pass the column name but you cannot use it in a sql statemnt like
Select #Columnname From Table
One could build a dynamic sql string and execute it like EXEC (#SQL)
For more information see this answer on dynamic sql.
Dynamic SQL Pros and Cons
As mentioned by MatBailie
This is much more safe since it is not a dynamic query and ther are lesser chances of sql injection . I Added one situation where you even want the where clause to be dynamic . XX YY are Columns names
CREATE PROCEDURE [dbo].[DASH_getTP_under_TP]
(
#fromColumnName varchar(10) ,
#toColumnName varchar(10) ,
#ID varchar(10)
)
as
begin
-- this is the column required for where clause
declare #colname varchar(50)
set #colname=case #fromUserType
when 'XX' then 'XX'
when 'YY' then 'YY'
end
select SelectedColumnId from (
select
case #toColumnName
when 'XX' then tablename.XX
when 'YY' then tablename.YY
end as SelectedColumnId,
From tablename
where
(case #fromUserType
when 'XX' then XX
when 'YY' then YY
end)= ISNULL(#ID , #colname)
) as tbl1 group by SelectedColumnId
end
First Run;
CREATE PROCEDURE sp_First #columnname NVARCHAR(128)--128 = SQL Server Maximum Column Name Length
AS
BEGIN
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT ' + #columnname + ' FROM Table_1'
EXEC(#query)
END
Second Run;
EXEC sp_First 'COLUMN_Name'
Please Try with this.
I hope it will work for you.
Create Procedure Test
(
#Table VARCHAR(500),
#Column VARCHAR(100),
#Value VARCHAR(300)
)
AS
BEGIN
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #Table + ' WHERE ' + #Column + ' = ' + #Value
--SELECT #sql
exec (#sql)
END
-----execution----
/** Exec Test Products,IsDeposit,1 **/

SQL query not showing columns in DataSet in Visual Studio

I have a stored procedure in SQL Server 2008. I declare an #query param via DECLARE #query NVARCHAR(MAX), I then set this #query according to the type of data sent in and then I use exec [sys].[sp_executesql] #query to execute the stored procedure.
The problem I'm having is that I'm using a DataSet in Visual Studio, that links to this stored procedure (used on a report).
When I do my stored procedure in this manner (with the #query), then the dataset does not pick up the column data to show. I had to create the stored procedure this way (with #query), because I need the where clause to be different depending on the data sent in.
My code:
CREATE PROCEDURE [dbo].[p_Test_GetFooData]
#pName VARCHAR(250) = '',
#pID INT = NULL
AS
BEGIN
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT FOO.FirstName, FOO.LastName
FROM Test.FooOne AS FOO'
IF ( #pName = '--- SELECT ---' )
BEGIN
SET #query = #query + ' WHERE FOO.ID = '
+ CONVERT(VARCHAR(50), #pID) + ''
END
ELSE
BEGIN
SET #query = #query + ' WHERE FOO.ID = '
+ CONVERT(VARCHAR(50), #pID) + ' AND
PP.FirstName + LIKE ''%' + #pName
+ '%'' '
END
EXEC [sys].[sp_executesql] #query
END
I have tested the query and it returns the correct data when I run the stored procedure. When I don't use SET #query = 'SELECT STATEMENT' EXEC #query then the dataset works as it should.
Any help will be appreciated.
Another option is to write the query in a different way eg.
SELECT FOO.FirstName,
FOO.LastName
FROM Test.FooOne AS FOO
WHERE FOO.ID = #pID
and PP.FirstName LIKE case when #pName = '--- Select ---' then PP.FirstName else '%' + #pName + '%' end
This will always compare Foo.ID to #pID
and will either compare pp.FirstName to itself (always returning true) or wil

Executing dynamic SQL in a SQLServer 2005 function

I will preface this question by saying, I do not think it is solvable. I also have a workaround, I can create a stored procedure with an OUTPUT to accomplish this, it is just easier to code the sections where I need this checksum using a function.
This code will not work because of the Exec SP_ExecuteSQL #SQL calls. Anyone know how to execute dynamic SQL in a function? (and once again, I do not think it is possible. If it is though, I'd love to know how to get around it!)
Create Function Get_Checksum
(
#DatabaseName varchar(100),
#TableName varchar(100)
)
RETURNS FLOAT
AS
BEGIN
Declare #SQL nvarchar(4000)
Declare #ColumnName varchar(100)
Declare #i int
Declare #Checksum float
Declare #intColumns table (idRecord int identity(1,1), ColumnName varchar(255))
Declare #CS table (MyCheckSum bigint)
Set #SQL =
'Insert Into #IntColumns(ColumnName)' + Char(13) +
'Select Column_Name' + Char(13) +
'From ' + #DatabaseName + '.Information_Schema.Columns (NOLOCK)' + Char(13) +
'Where Table_Name = ''' + #TableName + '''' + Char(13) +
' and Data_Type = ''int'''
-- print #SQL
exec sp_executeSql #SQL
Set #SQL =
'Insert Into #CS(MyChecksum)' + Char(13) +
'Select '
Set #i = 1
While Exists(
Select 1
From #IntColumns
Where IdRecord = #i)
begin
Select #ColumnName = ColumnName
From #IntColumns
Where IdRecord = #i
Set #SQL = #SQL + Char(13) +
CASE WHEN #i = 1 THEN
' Sum(Cast(IsNull(' + #ColumnName + ',0) as bigint))'
ELSE
' + Sum(Cast(IsNull(' + #ColumnName + ',0) as bigint))'
END
Set #i = #i + 1
end
Set #SQL = #SQL + Char(13) +
'From ' + #DatabaseName + '..' + #TableName + ' (NOLOCK)'
-- print #SQL
exec sp_executeSql #SQL
Set #Checksum = (Select Top 1 MyChecksum From #CS)
Return isnull(#Checksum,0)
END
GO
It "ordinarily" can't be done as SQL Server treats functions as deterministic, which means that for a given set of inputs, it should always return the same outputs. A stored procedure or dynamic sql can be non-deterministic because it can change external state, such as a table, which is relied on.
Given that in SQL server functions are always deterministic, it would be a bad idea from a future maintenance perspective to attempt to circumvent this as it could cause fairly major confusion for anyone who has to support the code in future.
Here is the solution
Solution 1:
Return the dynamic string from Function then
Declare #SQLStr varchar(max)
DECLARE #tmptable table (<columns>)
set #SQLStr=dbo.function(<parameters>)
insert into #tmptable
Exec (#SQLStr)
select * from #tmptable
Solution 2:
call nested functions by passing parameters.
You can get around this by calling an extended stored procedure, with all the attendant hassle and security problems.
http://decipherinfosys.wordpress.com/2008/07/16/udf-limitations-in-sql-server/
http://decipherinfosys.wordpress.com/2007/02/27/using-getdate-in-a-udf/
Because functions have to play nicely with the query optimiser there are quite a few restrictions on them. This link refers to an article that discusses the limitations of UDF's in depth.
Thank you all for the replies.
Ron: FYI, Using that will throw an error.
I agree that not doing what I originally intended is the best solution, I decided to go a different route. My two choices were to use sum(cast(BINARY_CHECKSUM(*) as float)) or an output parameter in a stored procedure. After unit testing speed of each, I decided to go with sum(cast(BINARY_CHECKSUM(*) as float)) to get a comparable checksum value for each table's data.