USE AdventureWorks2014
SELECT
result.BusinessEntityID,
p.FirstName, p.LastName,
result.TotalSales, '2011' AS SalesYear,
CASE
WHEN CAST(result.TotalSales AS int) > 1000000 THEN 'MillionPlus'
WHEN CAST(result.TotalSales AS int) < 100000 THEN 'Warning'
ELSE 'Met Expectations'
END AS BonusCategory
FROM
person.Person AS P,
(SELECT sp.BusinessEntityID, FORMAT(sum(soh.SubTotal), 'c', 'en-us') AS TotalSales
FROM sales.SalesPerson as sp
RIGHT JOIN sales.SalesOrderHeader as SOH ON sp.BusinessEntityID = soh.SalesPersonID
WHERE soh.orderdate LIKE '%2011%'
GROUP BY sp.BusinessEntityID) AS result
WHERE
result.BusinessEntityID = p.BusinessEntityID
OR result.TotalSales IS NULL
ORDER BY
result.BusinessEntityID
Full error message
Msg 245, Level 16, State 1, Line 2
Conversion failed when converting the nvarchar value ‘$28,926.25’ to data type int.
Advertisement
Answer
Something to understand when programming on almost ANY platform is converting between number values like 123.45
and string or text values like "123.45"
is a surprisingly slow and expensive operation. They may look the same, but those are different values, and largely thanks to internationalization/cultural concerns the conversions between them are not trivial. Always seek to minimize these conversions, and when you do need them leave it until the last possible moment.
SQL Server is no exception here. It can be very picky about mixing between numbers and text.
For this question, the nested inner query takes the sum(soh.SubTotal)
expression, which is a number, and wraps it in a FORMAT()
function call, which creates a text field. We later try to use the result in the outer query as if it were a number again.
Don’t do that!
Wait to format the result until the SELECT
clause of the outer statement, at the last possible moment. This allows us to remove the CAST()
s from inside the CASE
expression completely.
Also, NEVER use the old A,B WHERE
join syntax. It’s been out of date for more than 25 years now.
USE AdventureWorks2014
SELECT result.BusinessEntityID, p.FirstName, p.LastName,
FORMAT(result.TotalSales,'c','en-us') As TotalSales, '2011' as SalesYear,
CASE
WHEN result.TotalSales > 1000000 THEN 'MillionPlus'
WHEN result.TotalSales < 100000 THEN 'Warning'
ELSE 'Met Expectations'
END as BonusCategory
FROM person.Person as P
INNER JOIN (
SELECT sp.BusinessEntityID, FORMAT(sum(soh.SubTotal),'c','en-us') as TotalSales
FROM sales.SalesPerson as sp
RIGHT JOIN sales.SalesOrderHeader as SOH
ON sp.BusinessEntityID = soh.SalesPersonID
WHERE soh.orderdate LIKE '%2011%'
GROUP BY sp.BusinessEntityID
) result ON (result.BusinessEntityID = p.BusinessEntityID OR result.TotalSales is null)
ORDER BY result.BusinessEntityID
Even better, don’t even convert to a string/text in the SELECT clause! Let the client program or reporting tool handle this part. This is another way we can follow the “leave the conversion to the last possible moment” guidance in the first paragraph.