I have the following stored procedure, not writing the complete stored procedure but some of it is:
@course int = null,
SET @query = @query + 'Where course_id='+ cast(@course as varchar)
I want to know when I have converted the @course as the VARCHAR type while my course_id
in the database is of INT type - how the comparison takes place?
The comparison between two different types in SQL Server takes place like this:
For the sake of discussion: an INT
(precedence 16, high) and a VARCHAR
(precedence 27, low) would compare by casting the VARCHAR
to INT
and then comparing the two.
In your case though there is no conversion occuring, because what happens is that the @course
value gets appended to the dynamically constructed @sql
. Needless to say, that is bad. the proper solution is to pass down the @course
as a parameter to the dymnamic SQL invocation:
@course INT = null
...
SET @query = @query + 'Where course_id= @course';
...
exec sp_executesql @sql, '@course int', @course;
This is:
- faster: parameterized query instead of hard codded value
- safer: lower the risk of SQL injection if code gets refactored and @counter becomes a type tha can carry SQL injection
- less error-prone: no risk of NULL value propagating to nullify the entire
@sql
The rationale to the code you posted is that the data type change needs to occur for SQL Server to allow the string concatenation to happen. The statement itself is a string, submitted to the optimizer, which sees the comparison as the correct data types - INT to INT.
You can test & confirm for yourself using the following in Management Studio/Toad/etc:
DECLARE @course INT
SET @course = 1234
SELECT 'Where course_id='+ @course
This will fail, with an error reading:
Conversion failed when converting the varchar value 'Where course_id=' to data type int.
...while this:
DECLARE @course INT
SET @course = 1234
SELECT 'Where course_id='+ CAST(@course AS VARCHAR) AS output
...will return:
output
---------------------
Where course_id=1234
There are other means of approaching dynamic SQL. I highly recommend reading this article -- The Curse and Blessings of Dynamic SQL -- on the subject.
You seem to be confusing comparison (a = b
) and assignment (SET @a = b
)
In a comparison between VARCHAR
and INT
, the former is always being cast to the latter, so this query will fail:
SELECT 1
WHERE 'ab' = 1
, since 'ab'
is not castable to INT
.
In an assignment, the source value (b
) is always cast to the type of the target (@a
):
DECLARE @av VARCHAR(100)
SET @av = 1
GO
DECLARE @ai INT
SET @ai = 'ab'
GO
Since an INT
is always castable to a VARCHAR
, the first batch will always succeed, unlike the second batch which casts a VARCHAR
to an INT
.
In your query, you're actually just building another (dynamic) query. So, if you had for example:
SET @course = 2
SET @query = 'SELECT * FROM Courses'
SET @query = @query + 'Where course_id='+ cast(@course as varchar)
the value of @course is converted to characters, appended to the @query and in the end it's:
SELECT * FROM Courses Where course_id=2
which is perfectly valid sql without the need to convert anything before executing.