Conversion failed when converting value in dynamic sql statement - sql

I'm stuck on the following sql server:
DECLARE #sql AS NVARCHAR(500),#db as varchar(50),#value AS CHAR(129);
SET #db = 'SSCT1';
SET #value = '1806-11-801-1000';
SET #sql = 'SELECT ACTINDX FROM ' + quotename(#db)
+ '.[dbo].[GL00105] WHERE ACTNUMST = ' + #value;
EXEC (#sql);
When I run this in sql server I get :
Conversion failed when converting the varchar value '1806-11-801-1000
I checked the field I'm using the where clause against and it matches the type in the declaration (char(129) so I'm not sure what it's trying to convert.
I'm trying to build a sql statement that will accept the db name as a variable in addition to the value. any thoughts?
thanks

I'm going to guess that ACTNUMST is a string column, in which case you need to delimit it correctly:
SET #sql = 'SELECT ACTINDX FROM ' + quotename(#db)
+ '.[dbo].[GL00105] WHERE ACTNUMST = '''
+ #value + ''';';
If #value might ever contain apostrophes, you need to deal with that further:
SET #sql = 'SELECT ACTINDX FROM ' + quotename(#db)
+ '.[dbo].[GL00105] WHERE ACTNUMST = '''
+ REPLACE(#value, '''', '''''') + ''';';
Yuck. A much safer approach is:
DECLARE
#sql NVARCHAR(MAX),
#db SYSNAME,
#value CHAR(129);
SELECT
#db = N'SSCT1',
#value = '1806-11-801-1000';
SET #sql = N'SELECT ACTINDX FROM ' + quotename(#db)
+ '.[dbo].[GL00105] WHERE ACTNUMST = #val;';
EXEC sp_executesql #sql, N'#val CHAR(129)', #value;
This guards a little better against dynamic SQL and also prevents you from having to deal with the quotes.

This could happen because you didn't quote the char value. Try this
SET #sql = 'SELECT ACTINDX FROM ' + quotename(#db) + '.[dbo].[GL00105] WHERE ACTNUMST = ''' + #value + '''';

Does this fail when you try to execute the SQL? Or if you comment the EXEC call out, does it still fail?
One thing that catches my eye, is that you don't have any single quotes around your #value. So when the #sql is built, it will end up building it as ...
WHERE ACTNUMST = 1806-11-801-1000
however, it should look like this...
WHERE ACTNUMST = '1806-11-801-1000'
If this is indeed your issue, then you should modify your SET #sql to the following...
SET #sql = 'SELECT ACTINDX FROM ' + quotename(#db) + '.[dbo].[GL00105] WHERE ACTNUMST = ''' + #value + '''';
This will add the single quotes to your dynamic sql statement. I hope this helps.

Related

whether value is repeated in database?

IF EXISTS ( 'SELECT ' +#TextField+','+#ValueField+' FROM ' + #TableName + ' WHERE ' + #TextFieldPara + '=' +#ValueFieldPara + ' ORDER BY ' +#TextField+' ASC;')
BEGIN
SET #Message = #TextField + ' Already Exist...'
END
ELSE
BEGIN
SET #Message = 'TRUE'
END
by using this query i am trying to check in all table whether inserted value for field Repeated or Not.
when i write this query in store procedure i got an error at
IF EXISTS ( 'SELECT ' +#TextField+','+#ValueField+' FROM ' + #TableName + ' WHERE ' + #TextFieldPara + '=' +#ValueFieldPara + ' ORDER BY ' +#TextField+' ASC;') "
all variables are populated from c# back end.
is there any solution?
If you want to set the table, you need dynamic SQL. Here is one method:
declare #sql nvarchar(max);
declare #cnt int;
set #sql = '
SELECT #cnt = COUNT(*)
FROM #TableName
WHERE #TextFieldPara = #ValueFieldPara
');
set #sql = replace(#sql, '#TableName', #TableName);
set #sql = replace(#sql, '#TextFieldPara', #TextFieldPara);
set #sql = replace(#sql, '#ValueFieldPara', #ValueFieldPara);
exec sp_executesql(#sql, N'#cnt int output', #cnt=#cnt output);
if (#cnt > 0) . . .
begin
set #Message = #TextField + ' Already Exist...';
end
else
begin
set #Message = 'TRUE';
end;
I simplified the query because you don't need a select list or order by for determining the existing of values. I also used count(*) just because it is simpler to express. If you care about performance, there are better methods, such as a case with an exists in it.
Similarly, you should use quotename() for the identifiers in the query. You don't have that in your sample code, but it is a best practice.

SQL Server : how to insert using variable

I am trying to insert data into a SQL Server table using a variable. I tried
DECLARE #table NVARCHAR(50) = 'ToolList',
#val NVARCHAR(50) = 'test'
EXEC ('INSERT INTO ' + #table + 'SELECT ' + #val)
and
EXEC ('INSERT INTO ' + #table + '([col1]) VALUES(' + #val +')'
but still get an error that says
Incorrect syntax near 'test'.
you missed a space before SELECT and the #val should enclosed in single quote
DECLARE #table nvarchar(50) = 'ToolList',
#val nvarchar(50) = 'test'
EXEC ( 'INSERT INTO ' + #table + ' SELECT ''' + #val + '''')
when you use Dynamic SQL, it is easier to form the query in a variable so that you can print out , inspect the value before execution
select #sql = 'INSERT INTO ' + #table + ' SELECT ''' + #val + ''''
print #sql
exec (#sql)
You'd better use sp_executesql that allows for statements to be parameterized, to avoid the risk of SQL injection.
DECLARE #Query NVARCHAR(1000),
#table NVARCHAR(50) = 'ToolList'
SET #Query = 'INSERT INTO ' + #table + ' SELECT #val'
EXEC sp_executesql #Query, N'#val nvarchar(50)', #val = 'test'
sp-executesql-transact-sql
You can also use CHAR(39) instead of adding single quotes every time for better readability. And also, you have not added a space after the variable which contains the table name.
Query
declare #table nvarchar(50) = 'ToolList',
#val nvarchar(50) = 'test2';
declare #sql as varchar(max) = 'insert into ' + #table
+ ' select ' + char(39) + #val + char(39);
exec(#sql);
You need 4 singlequotes before the #val field as it is a string and all strings needs to be encapsulated in single quotes.
You can print the dynamic string using PRINT command check what the final string you are going to execute.
DECLARE #table VARCHAR(50) = 'ToolList'
DECLARE #val VARCHAR(50) = 'test'
DECLARE #DSQL AS VARCHAR(MAX) = ''
SET #DSQL = #DSQL + ' INSERT INTO [' + #table + ']' + '
SELECT ' + '''' + #val + ''''
--PRINT #DSQL
EXEC(#DSQL)

SQL Server: correct escaping Where clause in dynamic procedure (2)

I have a dynamic procedure where I want to use the below as part of my Where clause (everything else works as intended).
Currently this creates the following error: Incorrect syntax near the keyword 'LIKE'
AND CASE WHEN ' + #searchCategory + ' <> ''dateRec'' THEN
(R.' + #searchCategory + ' LIKE ''%' + #searchTerm + '%'')
ELSE
(R.dateRec = ' + CONVERT(VARCHAR, #searchTerm, 111) + ')
END
What would the proper escaping look like here ?
I believe this is what you're looking for:
declare #sql nvarchar(max), #searchCategory nvarchar(max), #searchTerm nvarchar(max)
set #searchCategory = 'dateRec'
set #searchTerm = 'yyy'
set #sql =
'AND (
(''' + #searchCategory + ''' <> ''dateRec'' AND (R.' + #searchCategory + ' LIKE ''%' + #searchTerm + '%''))
OR
(''' + #searchCategory + ''' = ''dateRec'' AND (R.dateRec = ''' + CONVERT(VARCHAR, #searchTerm, 111) + '''))
)'
print #sql
Assuming the contents of #searchCategory don't actaully contain the characters '...
SET #sql = 'AND ' +
CASE WHEN #searchCategory <> 'dateRec' THEN
'(R.' + #searchCategory + ' LIKE ''%'' + #searchTerm + ''%'')'
ELSE
'(R.dateRec = CONVERT(VARCHAR, #searchTerm, 111))'
END
This will give either....
AND (R.foobar LIKE '%' + #searchTerm + '%')
or...
AND (R.dateRec = CONVERT(VARCHAR, #searchTerm, 111))
This means that you would still pass #searchTerm to sp_executesql as a parameter, so as to protect you from SQL Injection attacks.
You DO NOT want to directly embed a user's free form text in to your SQL. Free form text must stay as a parameter in order to close that security hole.
(I'm also assuming that you have a white-list of valid values of #searchCategory so as to prevent that from being abused with an SQL Injection Attack?.)
EDIT :
An example of dynamic sql that maintains parameterisation....
DECLARE #SQL nvarchar(500);
SET #SQLString = N'SELECT * FROM table WHERE ' + #param1 + ' = #param;';
EXECUTE sp_executesql
#SQL,
'#param NVARCHAR(500)',
#param2
Using this method, you need to check that #param1 really is a legitimate field name, using a white-list for example, but you do not need to check #param2. This is because #param2 is being passed to sp_executesql as a parameter itself. It's just like dynamically making a stored procedure with parameters, rather than embedding all your values in the sql string, which lays you open to serious sql injection attacks.
EDIT :
This is not a case of embedding a LIKE statement within a CASE statement. What is being done here is creating a string that creates the string literal LIKE, by using a CASE statement.
It is much the same as this...
SET #sql = 'AND ' +
CASE WHEN #searchCategory <> 'dateRec' THEN
'A string with the word' + ' LIKE ' + 'in it'
ELSE
'A different string without that word in it'
END

sql with single quotes for parameter in where clause

I have below sql where I am trying to pass name parameter to sql, how can i append, I am getting syntax errors..
I have to #name in place of steeve
if #name not null BEGIN
set #sql = #sql + ' AND name=''steeve''';
END
if #name is not null then
BEGIN
set #sql = #sql + ' AND name=''' + #name + '''';
END

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.