ISNULL prints * - sql-server

I am encountering this strange behavior (Okay maybe not strange but beyond my understanding) when using isnull. It all sums up to this:
isnull(left(cast(null as varchar),1),0) gives 0
isnull(left(cast(null as varchar),1),-1) gives *
I would like to know the reason behind this behavior.
Although I got a workaround here:
select isnull(cast(left(cast(null as varchar),1) as varchar),-1)

The reason for the asterisk is due to an overflow error. left(cast(null as varchar),1) would return a varchar(1). Within the ISNULL the value -1 would be implicitly converted to a varchar(1), and a negative number cannot be represented with a single character, hence why an '*' is displayed.
If you change it to a LEFT(...2) then get a result:
SELECT ISNULL(LEFT(CAST(NULL AS varchar),2),-1);
On a different note Bad habits to kick : declaring VARCHAR without (length)

You could use COALESCE:
SELECT COALESCE(left(cast(null as varchar),1),-1);
DBFiddle Demo
ISNULL infers data type from first argument, COALESCE infers from wider one(Data type precedence)

Related

Unable to fix "may affect "CardinalityEstimate" in query plan choice" conversion warning (int to varchar)

Hi My task for a project is to remove this warning "Type conversion in expression (CONVERT(varchar(10),[Expr1017],0)) may affect "CardinalityEstimate" in query plan choice". I've been searching for a day and a half how to fix it and most other questions arent exactly what i need (i've tried their solutions but still have it). I can't just ignore it like some answers suggest bc the whole point of my task is to remove it.
CAST(product.Schedule AS VARCHAR(10)) AS [CSACode],
the schedule column is NOT computed and is generally either null or a single digit int.
I've tried reducing/increasing the VARCHAR size, using variables to pass around values and then convert that int var to the varchar (another site said issues arise sometime when converting a column to another type), using convert instead of cast, and just rework ton of code so it can return an int save it back in my MVC (it was too much work to do so, my boss said thats not the fix they want, and still resulted in other issues), but nothing seems to remove this warning beyond removing the cast entirely.
Removing this cast doesn't affect the row count at all it just removes the casted column CSACode in my select. I've been googling this CardinalityEstimate and row estimate seems to be all there is to it. It has little to no impact itself from what I can tell .
enter image description here
These 2 are the closest to questions I've found that are similar to my issue but not helpful.
How to avoid Implicit Type Conversion
Concatenation of INT columns warning: Type conversion in expression causes CardinalityEstimate warnings in execution plan
I am getting the same error with this minimal example:
SELECT CAST(product.Schedule AS VARCHAR(10)) AS [CSACode]
FROM (SELECT 123 AS Schedule UNION ALL SELECT NULL) AS product
The warning disappears when I change CAST(product.Schedule AS VARCHAR(10)) to FORMAT(product.Schedule, 'N0'):
SELECT FORMAT(product.Schedule, 'N0') AS [CSACode]
FROM (SELECT 123 AS Schedule UNION ALL SELECT NULL) AS product

Convert IS NULL to BIT

This might be a very basic question but I just came over it while writing a query.
Why can't SQL Server convert a check for NULL to BIT? I was thinking about something like this:
DECLARE #someVariable INT = NULL;
-- Do something
SELECT CONVERT(BIT, (#someVariable IS NULL))
The expected outcome would then be either 1 or 0.
Use case:
SELECT CONVERT(BIT, (CASE WHEN #someVariable IS NULL THEN 1 ELSE 0 END))
Or use IIF (a little more readable than CASE):
CONVERT(BIT, IIF(#x IS NULL, 0, 1))
not a direct cast
select cast(isnull(#null,1) as bit)
In SQL the language, NULLs are not considered data values. They represent a missing/unknown state. Quoting from Wikipedia's article on SQL NULL:
SQL null is a state (unknown) and not a value. This usage is quite different from most programming languages, where null means not assigned to a particular instance.
This means that any comparison against that UNKNOWN value can only be UNKNOWN itself. Even comparing two NULLs can't return true: if both values are unknown, how can we say that they are equal or not?
IS NULL and IS NOT NULL are predicates that can be used in conditional expressions. That means that they don't return a value themselves. Therefore, they can't be "cast" to a bit , or treated as a boolean.
Basic SQL comparison operators always return Unknown when comparing anything with Null, so the SQL standard provides for two special Null-specific comparison predicates. The IS NULL and IS NOT NULL predicates (which use a postfix syntax) test whether data is, or is not, Null.
Any other way of treating nulls is a vendor-specific extension.
Finally, BIT is not a boolean type, it's just a single-bit number. An optional BOOLEAN type was introduced in SQL 1999 but only PostgreSQL implements it correctly, ie having TRUE, FALSE or UNKNOWN values.
Without a BOOLEAN type you can't really calculate the result of a conditional expression like A AND B or x IS NULL. You can only use functions like NULLIF or COALESCE to replace the NULL value with something else.

what is the default type when a plain number is hard coded in a mssql statement?

I have some code that
is doing
SELECT * FROM table where col = 123
col is varchar(64)
when col was containing values less than 1456324852868
it was working fine till at some point someone added value 635912311949815157 in the table which of course caused this error
Arithmetic overflow error converting varchar to data type numeric.
This is because in order to compare two values they need to be in compatible types. So for some reason values less than 1456324852868 were converted fine but 635912311949815157 couldn't be converted.
My question is when there is hardcoded sql integer values (like 123 above) what data type they are and why it was working for varchar value 1456324852868 which is above int range but when varchar value 635912311949815157 was added things started breaking?
I did some experimentation and
SELECT * FROM table where col = convert(int,123) gives a different error (Arithmetic overflow error converting expression to data type int.) so
the answer is not int.
btw I know that putting quotes around the number will fix this new bug but I would like to know what is happening with the data types. I know it is a terrible practice but the code is there and I didn't write it.
I am using MS-SQL Server 2012
Thanks!
The value 123 is stored as an integer, you can verify this in a similar statement using sp_describe_first_result_set
EXEC sp_describe_first_result_set N'SELECT 123'
When evaluating your query, col will be implicitly converted to an integer, however, it will depend on the chosen execution plan which records will be converted and therefore you may have gotten away with 1456324852868.
I had to debug a similar issue in our live environment. The query in question had been running without problems for years, until new data was added to one of the tables and the chosen execution plan changed. This resulted in failure of the query, because the new execution plan resulted in evaluation of a VARCHAR value that couldn't be converted to an integer.
For starters one should not be inserting hard-coded integer values into varchar (or any kind of character) columns. DOING SO IS A BAD PRACTICE! All literal values inserted into a character datatype should be enclosed in single quotes.
That said the following code answers your main question:
use tempdb
go
select
20 as 'tiny',
1000 as 'small',
33000 as 'medium',
100000 as 'large',
3000000000 as 'huge'
into #tempvalues
go
exec sp_help 'tempdb.dbo.#tempvalues_________________________________________________________________________________________________________000000000003'
go
You will have to do this in tempdb and lookup the appropriate name for the sp_help
The result is that all integer literals are implicitly cast as int unless they are too big in which case they are cast as numeric

Subtract Operator in Stored Proc for Concatenate

I am getting the data type error in my stored procedure because I'm using Field1+'-'+Field2. I tried a convert and a cast, but it's not liking the syntax I used for that pesky -.
For the subtraction operation, what's the best way to use it as a hyphen or dash instead of an operator?
Thank you!!!
You need to convert both field to strings, like this:
Convert(VarChar(10), Field1) + '-' + Convert(VarChar(10), Field2)
If either field is a number, sql server will treat this as a math operation instead of concatenation.
** I used varchar(10) as an example. You should double check your data types and adjust the 10 accordingly.
If you are on 2012+
CONCAT(Field1,'-',Field2)
is a bit less verbose. Docs
All arguments are implicitly converted to string types and then
concatenated. Null values are implicitly converted to an empty string

Issue with join: Error converting data type nvarchar to float?

I've been trying to run the program below and I keep on getting the error
Error converting data type nvarchar to float
SQL:
SELECT
distinct
coalesce(a.File_NBR,b.File_NBR) as ID,
b.Division,
b.Program,
a.Full_Name,
a.SBC_RESULT
FROM
New_EEs.dbo.vw_SBC_RESULTS a
full join
New_EEs.dbo.vw_SBC_Employee_Info b on a.File_NBR = b.File_NBR
where
(a.File_NBR is not null OR b.File_NBR is not null)
and A.Full_Name is not null
order by
a.Full_Name, b.Division, b.Program
When I comment out /*and A.Full_Name is not null */ the program works.
I can't figure out what the error means and why the join works when I comment out /*and A.Full_Name is not null */
Any feedback is appreciated.
Thanks!
The error message clearly says that the issue has to do with conversion of an nvarchar to a float. There's no explicit conversion in your query, therefore it's about implicit one. If the issue indeed stems from this particular query and not from somewhere else, only two places can be responsible for that:
1) the join predicate;
2) the COALESCE call.
Both places involve one and the same pair of columns, a.File_NBR and b.File_NBR. So, one of them must be an nvarchar column and the other a float one. Since the float type has higher precedence than nvarchar, the latter would implicitly be converted to the former, not the other way around. And apparently one of the string values failed to convert. That's the explanation of the immediate issue (the conversion).
I've seen your comment where you are saying that one of the columns is an int and the other float. I have no problem with that as I believe you are talking about columns in physical tables whereas both sources in this query appear to be views, judging by their names. I believe one of the columns enjoys a transformation to nvarchar in a view, and this query ends up seeing it as such. So, that should account for you where the nvarchar can come from.
As for an explanation to why commenting a seemingly irrelevant condition out appears to make such a big difference, the answer must lie in the internals of the query planner's workings. While there's a documented order of logical evaluation of clauses in a Transact-SQL SELECT query, the real, physical order may differ from that. The actual plan chosen for the query determines that physical order. And the choice of a plan can be affected, in particular, by such a trivial thing as incorporation or elimination of a simple condition.
To apply that to your situation, when the offending condition is commented out, the planner chooses such a plan for the query that both the join predicate and the COALESCE expression evaluate only when all the rows capable of causing the issue in question have been filtered out by predicates in the underlying views. When the condition is put back, however, the query is assigned a different execution plan, and either COALESCE or (more likely) the join predicate ends up being applied to a row containing a string that cannot be converted to a float, which results in an exception raised.
Conversion of both a.File_NBR and b.File_NBR to char, as you did, is one way of solving the issue. You could in fact pick any of these four string types:
char
varchar
nchar
nvarchar
And since one of the columns is already a string (possibly the a.File_NBR one, but you are at a better position to find that out exactly), the conversion could be applied to the other one only.
Alternatively, you could look into the view producing the nvarchar column to try and see if the int to nvarchar conversion could be eliminated in the first place.
Please see this example, maybe will be useful.
CREATE TABLE TEST(
ID FLOAT)
INSERT INTO TEST(ID) VALUES(NULL)
INSERT INTO TEST(ID) VALUES(12.3)
--SELECT COALESCE(ID,'TEST') FROM TEST; NOT WORKING ERROR:Error converting data type nvarchar to float
SELECT COALESCE(CAST(ID AS VARCHAR),'TEST') FROM TEST; --WORKS

Resources