Updating table using dynamic query - sql-server

SET #TempTable = 'UPDATE #TempTable SET AvgVal = ' + #AvgVal + '
WHERE PropertyName =''' + #PropertyName + '''' + ' AND CrudeName =''' + #crudeName + ''''
I have an update query which is dynamic. AvgVal is float. The variable has the same datatype in the table as well. Still I get an error saying -
Error converting data type varchar to numeric.
If i convert the value to varchar it works. Whats the workaround for this.

What you call the "workaround" is the answer. SQL Server uses the + operator as both addition and string concatenation. However, because the addition operator has a slightly higher precedence than the concatenation operator, the addition operator is assumed to apply first.
See the SQL Server BOL on Operator Precedence. At the 3rd level of precedence, the order goes:
+ (Positive)
- (Negative)
+ (Add)
+ (Concatenate)
- (Subtract)
& (Bitwise AND),
^ (Bitwise Exclusive OR)
| (Bitwise OR)
To see how this affects queries take the following example:
select '1' + 2
The result of this query is not an error. SQL Server interprets the + as addition. It sees the number 2 is an integer, so it tries to convert the string '1' into an integer and is successful. The result is the integer 3.
However, try this:
select '1.0' + 2
SQL Server again sees this as an addition, notes that 2 is an integer, and tries to convert the string '1.0' into an integer. This conversion fails with the following error:
Msg 245, Level 16, State 1, Line 1 Conversion failed when converting
the varchar value '1.0' to data type int.
In your example, SQL Server detects the float #AvgVal and attempts to convert the string 'UPDATE #TempTable SET AvgVal = ' into a float. The conversion fails spectacularly, and the error you reported occurs.
One possibility is to convert the #AvgVal into a string first:
SET #TempTable = 'UPDATE #TempTable
SET AvgVal = ' + CAST(#AvgVal AS VARCHAR) + '
WHERE PropertyName =''' + #PropertyName + '''' + '
AND CrudeName =''' + #crudeName + '''
Note: This query is vulnerable to a SQL injection attack! To avoid a SQL injection attack, always pass data to a dynamic query as parameters:
SET #TempTable = N'UPDATE #TempTable SET AvgVal = #AvgVal
WHERE PropertyName = #PropertyName AND CrudeName = #crudeName'
exec sp_executesql #TempTable,
N'#AvgVal float, #PropertyName varchar(255), #crudeName varchar(255)',
#PropertyName = #PropertyName,
#CrudeName = #crudeName
This both avoids a SQL injection attack and avoids the conversion error.

I see. Sorry, misunderstood.
Yes, because of the way SQL Server works. You are building a concatenated string. You will have to do the CAST. Type conversions in SQL Server are a little wonky. You would think it would inherently cast to the simplest data type, varchar, but in this instance it isn't. So you will have to do ... + CAST(#AvgVal AS VARCHAR(255)) + ...

Keep in mind some times variables like #PropertyName if is Nvarchar type you should do
in front of the string N'
DECLARE #TempTable NVARCHAR
SET #TempTable = N'UPDATE and so on for the rest

Related

Passing Int to dynamic stored procedure fails

I have a dynamic stored procedure in SQL Server that works well to pivot a table:
CREATE PROCEDURE dbo.DynamicPivotTableInSql
#ColumnToPivot NVARCHAR(255),
#ListToPivot NVARCHAR(255),
#SurveyID INT=10
AS
BEGIN
DECLARE #SqlStatement NVARCHAR(MAX)
SET #SqlStatement = N'SELECT *
FROM
(SELECT
[resp_id], [benefit], [weight]
FROM Segment_Responses) myResults
PIVOT
(SUM([weight])
FOR [' + #ColumnToPivot + ']
IN (' + #ListToPivot + ')) AS PivotTable';
EXEC (#SqlStatement)
END
and I call it like this
EXEC DynamicPivotTableInSql
#ColumnToPivot = 'benefit',
#ListToPivot = '[OBSERVABILITY], [COST], [EASE OF USE], [SERVICE]'
Here is where I run into problems. You'll notice I have hardcoded #SurveyID = 10 and if I try to add that as a where statement in the stored procedure like this:
FROM Segment_Responses
WHERE survey_id = ' + #SurveyID + '
and run the stored procedure again I get this error:
Conversion failed when converting the nvarchar value '
SELECT * FROM (
SELECT
[resp_id],
[benefit],
[weight]
FROM Segment_Responses where survey_id=' to data type int.
I've tried to solve this many ways (e.g., passed the Int variable instead of hard coding it) but always get the same result. Any ideas what is going on?
Just to try to add some clarity, when you add together two different types, SQL Server will (where it can) implicitly convert one to the other - the result must be a single type after all.
It decides which one to convert "to the other" based on an order of precedence.
So where you are trying to concatenate a varchar with an int, the int has the higher order of precedence. This is also a common cause of errors and bugs when using a case expression when mixing types in different execution paths of the expression.
You need to be explicit and cast the int to a varchar.
Ideally you would use a parameterised query which would also reuse the cached execution plan - this may be beneficial if the cardinality of the data is similar but sometimes making the value part of the query dynamically can be advantagous, it depends on the use-case.
This is why the syntax EXEC (#SQL) is strongly suggested against. Use sys.sp_executesql and parametrise your statement:
SET #SQL = N'SELECT ...
FROM ...
WHERE survey_id = #SurveyID ...;';
EXEC sys.sp_executesql #SQL, N'#SurveyID int',SurveyID;
The + only works with strings. If you use a number TSQL assumes you are trying to use the addition operator, and tries to convert the string argument to int.
eg this
select 1 + '2'
works and returns 3.
Use CONCAT instead of +, or use an explicit conversion on the int.
eg
WHERE survey_id = ' + cast(#SurveyID as varchar(20)) + '

Error converting data type varchar to numeric. in Below sql Querys

SELECT ltrim(flexid + ' - ' + descr + ' ($' + max_deduct + ' deductible)') descr,
flexid
FROM [MHCSI].[FLEX_PLAN]
WHERE groupno = '0000080002' ORDER BY flexid;
The issue is that SQL Server assumed you want to perform a mathematical function to the max_deduct column. Because what you really want to do is just concatonate it to the rest of the string value that you are building, the easiest solution would be to make sure SQL Server understands you want to use the value of the column as a test value. You can do this by explicitly casting the value to a text-string.
Try rewriting your statement to this:
SELECT
ltrim(flexid + ' - ' + descr + ' ($' + CAST(max_deduct AS nvarchar(50)) + ' deductible)') descr
, flexid
FROM
[MHCSI].[FLEX_PLAN]
WHERE
groupno = '0000080002' ORDER BY flexid;

Convert Raw[16](Guid) in Oracle to uniqueidentifier in SQL Server by SSMA

I have dealt with the problem that I need to convert data from the Oracle database to the SQL Server database and there is no way except SQL Server Migration Assistant.
In SSMA there is a TypeMapping which allows you to define types you want to cast. I added Raw[16](Guid in Oracle) to UniqueIdentifier(Guid in oracle) as TypeMapping.
But when SSMA started to convert data it returns this exception:
The given value of type Byte[] from the data source cannot be
converted to type uniqueidentifier of the specified target column.
ORACLE SYS_GUID is RAW(16) and it is 32 character hexadecimal representation.
The equivalent, SQL Server datatype Uniqueidentifier, is 16 byte binary value, which is 36 character representation.
SQL Server UniqueIdentifier
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, in which each x is a hexadecimal
digit in the range 0-9 or a-f. For example,
6F9619FF-8B86-D011-B42D-00C04FC964FF is a valid uniqueidentifier value
You can think of below options:
I would suggest you to use VARCHAR(32) to represent corresponding ORACLE GUID in SQLServer in the typemapping.
you can have a default value for NEWID() for the target column, values will get assigned when you load the data
Have the target datatype as VARCHAR(36) and once you are done with migration, you can start using NEWID() for future values. As GUIDs are going to be unique, you will not face issues.
DECLARE #uniqORAconvMS varchar(32) = '5cf5d1b5db12d38067affb261d9619dc'
SELECT left(#uniqORAconvMS,8) + '-' + SUBSTRING(#uniqORAconvMS,9,4) + '-' + SUBSTRING(#uniqORAconvMS,13,4) + '-' + SUBSTRING(#uniqORAconvMS,17,4) + '-' + RIGHT(#uniqORAconvMS,12)
-- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
-- 5cf5d1b5-db12-d380-67af-fb261d9619dc
-- With TRY_CONVERT to check valid uniqueidentifier
SELECT TRY_CONVERT (UNIQUEIDENTIFIER, LEFT(#uniqORAconvMS,8) + '-' + SUBSTRING(#uniqORAconvMS,9,4) + '-' + SUBSTRING(#uniqORAconvMS,13,4) + '-' + SUBSTRING(#uniqORAconvMS,17,4) + '-' + RIGHT(#uniqORAconvMS,12))
The answer by Martin is slightly incorrect. If you use entity framework, you'll notice the GUIDs returned from oracle aren't just split up by dashes, but also reordered. So 5cf5d1b5db12d38067affb261d9619dc should become b5d1f55c-12db-80d3-67af-fb261d9619dc, not 5cf5d1b5-db12-d380-67af-fb261d9619dc.
DECLARE #uniqORAconvMS varchar(32) = '5cf5d1b5db12d38067affb261d9619dc';
SELECT SUBSTRING(#uniqORAconvMS,7,2) + SUBSTRING(#uniqORAconvMS,5,2) + SUBSTRING(#uniqORAconvMS,3,2) + SUBSTRING(#uniqORAconvMS,1,2) + '-' + SUBSTRING(#uniqORAconvMS,11,2) + SUBSTRING(#uniqORAconvMS,9,2) + '-' + SUBSTRING(#uniqORAconvMS,15,2) + SUBSTRING(#uniqORAconvMS,13,2) + '-' + SUBSTRING(#uniqORAconvMS,17,4) + '-' + RIGHT(#uniqORAconvMS,12);

Passing a Date variable in a Openrowset query

I have this Openrowset Query where I'm able to pass a Path to get the correct Excel file. I can't figure out the exact number of quotes and/or the type conversion
I need to pass a WHERE statement to get a specific date in the table. I'm coming from VBA where you put 2 quotes and 2 & and you're all set. It seems SQL needs hundreds of them! (If someone has a link to an online university about SQL quotes that'd be much appreciated too)
set #sql1 = 'select *
FROM OPENROWSET(''Microsoft.ACE.OLEDB.12.0'',
''Excel 12.0;Database=' + #filePath1 + '' + ';' + '''' + ',
''Select * from [Sheet1$] '''+')'
exec(#sql1)
I'd like to add WHERE DATE = #PnLDate
The rule is very similar to what you know but it applies to single quote '. When you want one of them inside a string you escape it using two ''. Apart of them, you must have one for openning and one for closing.
The string you show, is a bit complicated just because it contains innecesary empty strings, and secuentialy contatenated stirngs that can be replaced just for only one resultant string. Below a simiplificated version of your string:
set #sql1 = 'select *
FROM OPENROWSET(''Microsoft.ACE.OLEDB.12.0'',
''Excel 12.0;Database=' + #filePath1 + ';'',
''Select * from [Sheet1$] '')'
With your requested where may be:
DECLARE #PnLDate datetime -- may be varchar instead, if so, must remove convert(....) and use straight #PnLDate
set #PnLDate = '2014/08/01'
set #sql1 = 'select *
FROM OPENROWSET(''Microsoft.ACE.OLEDB.12.0'',
''Excel 12.0;Database=' + #filePath1 + ';'',
''Select * from [Sheet1$] where DATE = #'+ -- hash # is MS Access delimiter for dates
convert(varchar(25), #PnLDate, 111)
+'#'')' -- results in #2014/08/01#
Be warned that when you use "dynamic SQL" your data can inject malicious code on the string. read about sql Injection

Why does SQL Server require INT to be converted to NVARCHAR?

During an ordeal yesterday, I learned that you can't pass this query to EXEC():
#SQL = #SQL + 'WHERE ID = ' + #SomeID
EXCEC(#SQL)
Where #SomeID is an INT and #SQL is NVARCHAR. This will complain about not being able to convert NVARCHAR back to INT during execution.
I realized you have to do it like
#SQL = #SQL + 'WHERE ID = ' + CONVERT(NVARCHAR(20), #SomeID)
What I didn't understand is why? Why doesn't SQL Server understand the INT when simply +:ed on to an NVARCHAR? I'm guessing it has something to do with char sets.
EDIT: Fixed typos (missed some +:s).
+ (String Concatenation)
An operator in a string expression
that concatenates two or more
character or binary strings, columns,
or a combination of strings and column
names into one expression (a string
operator).
Expression is any valid Microsoft® SQL Server™
expression of any of the data types in
the character and binary data type
category, except the image, ntext, or
text data types. Both expressions must
be of the same data type, or one
expression must be able to be
implicitly converted to the data type
of the other expression.
So... There is no implicit convertion of int to string... This is an internal question
I'm not saying this will definitely work, and I'm not near my sql management studio to try it before posting, but have you tried something like this:
#SQL = #SQL + 'Where ID = ' + #SomeID
EXEC(#SQL)

Resources