Update DATEADD with nvarchar? - sql-server

I am stuck with a table that has a column, [Renewal] that was set as a nvarchar(255),null. Not my handiwork and I cant change it. I now need to use this column in a calculation. Below as close as I can get with doing it in one statement but I cant get past the "incorrect syntax near..." error after the ELSE statement. If it ran into an issue trying to CAST as INT I would want it to continue with the rest of the records and not hose the whole query. I suppose I could add an additional column thats INT, not null and write an update statement to run prior to this but I fear I would run to the same issue. I am more UI guy than SQL guy, any suggestions? It would run on SQL Server 2008R. Thank you in advance
UPDATE C
SET [ExpirationDate] = DATEADD(MONTH,
(CASE WHEN ISNUMERIC(C.Renewal)= 1
THEN CAST(C.Renewal AS INT)
ELSE 0)
, [ExpirationDate])
FROM dbo.MyTable C
WHERE C.MyCondition = 'True'

You are missing END in CASE statement
UPDATE C
SET [ExpirationDate] = Dateadd(MONTH, ( CASE
WHEN Isnumeric(C.Renewal) = 1 THEN Cast(C.Renewal AS INT)
ELSE 0
END ), [ExpirationDate])
FROM dbo.MyTable C
WHERE C.MyCondition = 'True'
Filter the records with only numeric values and apply DATEADD function instead of adding 0 months
Also ISNUMERIC is not preferred. ISNUMERIC returns 1 if the string can be converted to any one of ints, numeric/decimal, float, or money.
https://connect.microsoft.com/SQLServer/feedback/details/302466/isnumeric-returns-true-for-and
UPDATE C
SET [ExpirationDate] = DATEADD(MONTH,CAST(C.Renewal AS INT), [ExpirationDate])
FROM dbo.MyTable C
WHERE C.MyCondition = 'True'
AND C.Renewal NOT LIKE '%[^0-9]%'

This would be more straightforward.
UPDATE C
SET [ExpirationDate] = DATEADD(MONTH, CAST(c.Renewal AS INT), [ExpirationDate])
FROM dbo.MyTable C
WHERE C.MyCondition = 'True'
AND ISNUMERIC(c.Renewal) = 1

Related

SQL Server Conversion error: Conversion failed when converting the nvarchar value 'XXX' to data type int

Running some pretty simple SQL here:
select *
from table
where columnA <> convert(int,columnB)
and isnumeric(columnB) = 1
Still getting this error every time:
Conversion failed when converting the nvarchar value 'XXX' to data type int.
If you're using SQL Server 2012 or more recent you could use TRY_PARSE which will return NULL when the parse fails.
SELECT TRY_PARSE('one' as int) -- NULL
, TRY_PARSE('1' as int) -- 1
, TRY_PARSE('0.1' as int) -- NULL
Returns the result of an expression, translated to the requested data type, or null if the cast fails in SQL Server. Use TRY_PARSE only for converting from string to date/time and number types.
Isnumeric has a lot of odd behavior. For example, it also considers currency signs such as $ or £, and even a hyphen (-) to be numeric.
I think you'd be better of using NOT columnB like '%[^0-9]%' to ONLY take numbers into account.
Check the comments at the bottom of the msdn page for isnumeric(), which you can find here: https://msdn.microsoft.com/en-us/library/ms186272.aspx
This may sound weird, but it breaks when do not put the ISNUMERIC check first. Try this out:
WITH [Table]
AS
(
SELECT columnA,columnB
FROM
(
VALUES (1,'2'),
(2,'XXX')
) A(columnA,columnB)
)
select *
from [Table]
where ISNUMERIC(columnB) = 1 --this works
AND columnA <> convert(int,columnB)
--where columnA <> convert(int,columnB) --this doesn't work
-- and isnumeric(columnB) = 1
I suggest you to reverse your checking like this:
SELECT *
FROM table
WHERE CONVERT(NVARCHAR, columnA) <> columnB
I got this using a combination of the answers and comments here. I used a CASE statement in my WHERE clause and also had to use LIKE instead of ISNUMERIC to account for illegal characters. I also had to use BIGINT because a few select samples were overflowing the INT column. Thanks for all of the suggestions everybody!
select * from patient
where PatientExternalID <>
(case when mrn not like '%[^0-9]%'
then convert(bigint, mrn)
else 0
end)

Getting conversion failed error even when CONVERT function is not being called SQL

I use this command to select all the specific dates if the given variable is date, if it is not it should return all of the fields.
The commands works when #query is in the form of a date, but it returns an error:
"Conversion failed when converting date and/or time from character string."
when it is any other arbitrary string.
Code:
select * from table where
format(dateofbirth,'dd/MMM/yyyy') = Case
when ISDATE(#query)=1 then
format(CONVERT(datetime,#query),'dd/MMM/yyyy')
else
format(dateofbirth,'dd/MMM/yyyy')
Edit:
#query can be any string for eg. "1/1/2013" , "random" , "3".
The command should return all fields if #query is not in form of a date.
You can work around this problem by re-formulating your query condition like this:
declare #query as varchar(20)='blah'
SELECT *
FROM testtable
WHERE ISDATE(#query) = 0
OR CONVERT(date, dateofbirth) = CASE ISDATE(#query)
WHEN 1 THEN CONVERT(date, #query) ELSE NULL
END
Demo on sqlfiddle.
The problem is that logic operators are not short-circuited in SQL, so the optimizer treats CONVERT(date, #query) as something that it can pre-compute to speed up the query. By expanding the condition to a CASE that depends entirely on #query you can eliminate the execution of the CONVERT branch when ISDATE(#query) returns "false".

Evaluating GETDATE twice in a statement - will it always evaluate to be the same?

suppose
isnull(some_column, getdate()) >= getdate()
where logic is if some_column is null this expression should always be true. However will this always be so (since between two evaluations of getdate() some time has passed and they won't be equal) ?
No, is not safe. You are facing so called runtime constants expressions, of which GETDATE() is the bookcase example, which are evaluate once at query startup and the subsequently the cached evaluated value is used. However each occurence is evaluated separately once and the two evaluation can fall on the separate sides of the datetime precision boundary, resulting in two different values.
A simple test reveals how this happens:
declare #cnt int = 0, #i int = 0
while #cnt = 0
begin
select #cnt = count(*)
from master..spt_values
where getdate() != getdate();
set #i += 1;
if #cnt != 0
raiserror(N'#cnt = %d this shoudl not happen but it dit after #i = %d', 16, 1, #cnt, #i);
end
In my case this was hit right away:
Msg 50000, Level 16, State 1, Line 9
#cnt = 2515 this shoudl not happen but it dit after #i = 694
I'm not addressing the question how to better do this (you already got plenty of advice) but the underlying question whether your assumption about the run-time execution is right (is not):
GETDATE() twice in a statement will be evaluate twice
Since you are looking for true in the condition, you don't need to use getDate() twice. Just put in a very large date instead...
For example:
isnull(some_column, '2999-01-01') >= getDate()
as in
declare #some_column(datetime)
select case when isnull(#some_column,'2999-01-01') >= getdate() then 1 else 0 end
which returns 1.
Alternatively you can do it properly and check for the null explicitly:
(some_column >= getdate() or some_column is null)
In SQL Server 2000 and previous versions, getdate() is a deterministic function evaluated ONCE per SQL sentence. From 2005 and on, getdate is NOT deterministic, it's evaluated each time so you should assign the value to a variable.
Since you are invoking GETDATE() twice, this may fail, though most of the time it will work right.
You can do the following to mitigate:
DECLARE currentDate DATETIME
SELECT currentDate = GETDATE()
isnull(some_column, currentDate) >= currentDate
Why do you want to use date. I mean there is no reason to ask sql server to evaluate/process for a default true condition.
You can instead use
isnull(some_column, 2) >= 1

Conversion failed when converting the varchar value 'N' to data type int

This is in SQL Server 2008 R2.
I'm pulling my hair with this one. Follow closely, please.
When I run this, I get 27 rows returned:
select *
from dbo.[12STD_NO_VISIT]
where (
(dbo.fPhoneExists(PhoneNumber1) = 1
AND (NextCall1 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
)
And when I run this, I get 21 rows returned (notice the change to PhoneNumber2 and NextCall2):
select *
from dbo.[12STD_NO_VISIT]
where (
(dbo.fPhoneExists(PhoneNumber2) = 1
AND (NextCall2 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
)
But, when I run this, 'ORing' the 2 conditions, I get an error:
Conversion failed when converting the varchar value 'N' to data type int
select *
from dbo.[12STD_NO_VISIT]
where (
(dbo.fPhoneExists(PhoneNumber1) = 1
AND (NextCall1 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
OR
(dbo.fPhoneExists(PhoneNumber2) = 1
AND (NextCall2 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
)
But it doesn't just give me the error. It first retrieves 42 rows, displaying that for a split second (Results tab), and then it displays the error (Messages tab).
I can't figure this one out. Any help is very appreciated.
Thanks!
FUNCTION [dbo].[fPhoneExists](#PhoneNumber varchar)
RETURNS BIT
WITH EXECUTE AS CALLER
AS
BEGIN
DECLARE #GoodNumber bit
IF (#PhoneNumber is NULL or #PhoneNumber = 0 or #PhoneNumber = '')
SET #GoodNumber = 0;
ELSE
SET #GoodNumber = 1;
Return(#GoodNumber);
END
What are the data types of PhoneNumber1 and PhoneNumber2? What is the definition of dbo.fPhoneExists? I suspect the problem lies there somewhere - or as Lynn suggests maybe there is more to the query than you've shown us (the query would either succeed or fail as a whole; it wouldn't produce 42 rows and then an error).
Now that we see the function, here is a re-write:
ALTER FUNCTION [dbo].[fPhoneExists]
(
#PhoneNumber VARCHAR -- varchar(what)? This should match definition of column
)
RETURNS BIT
WITH EXECUTE AS CALLER
AS
BEGIN
RETURN (SELECT CASE WHEN COALESCE(#PhoneNumber, '') IN ('0', '') THEN 0 ELSE 1 END);
END
GO
There is no reason to store the temporary logic in a variable, also notice that you are returning the variable only in the ELSE condition.
Your dbo.fPhoneExists function contains an implicit cast in the PhoneNumber = 0 expression that, according to the rules of Data Type Peecendence casts the PhoneNumber VARCHAR value to an int. This cast will fail if the value in the string is not a numeric. You are also falling into the fallacy of assuming that boolean operator short circuit is guaranteed in SQL, which is simply not true. SQL is a declarative language and the order of evaluation of boolean operators is not guaranteed.
You'll get better performance replacing fnPhoneExists by replacing the function with a CASE:
select *
from dbo.[12STD_NO_VISIT]
where (
(case when ISNULL(PhoneNumber1,'') not in ('0','') then 1 else 0 end=1
AND (NextCall1 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
OR
(case when ISNULL(PhoneNumber2,'') not in ('0','') then 1 else 0 end=1
AND (NextCall2 BETWEEN GETDATE() AND DATEADD(hh, 1, GETDATE())))
)
This is because the optimizer won't optimize the contents of fnPhoneExists.

Saving a select count(*) value to an integer (SQL Server)

I'm having some trouble with this statement, owing no doubt to my ignorance of what is returned from this select statement:
declare #myInt as INT
set #myInt = (select COUNT(*) from myTable as count)
if(#myInt <> 0)
begin
print 'there's something in the table'
end
There are records in myTable, but when I run the code above the print statement is never run. Further checks show that myInt is in fact zero after the assignment above. I'm sure I'm missing something, but I assumed that a select count would return a scalar that I could use above?
If #myInt is zero it means no rows in the table: it would be NULL if never set at all.
COUNT will always return a row, even for no rows in a table.
Edit, Apr 2012: the rules for this are described in my answer here:Does COUNT(*) always return a result?
Your count/assign is correct but could be either way:
select #myInt = COUNT(*) from myTable
set #myInt = (select COUNT(*) from myTable)
However, if you are just looking for the existence of rows, (NOT) EXISTS is more efficient:
IF NOT EXISTS (SELECT * FROM myTable)
select #myInt = COUNT(*) from myTable
Declare #MyInt int
Set #MyInt = ( Select Count(*) From MyTable )
If #MyInt > 0
Begin
Print 'There''s something in the table'
End
I'm not sure if this is your issue, but you have to esacpe the single quote in the print statement with a second single quote. While you can use SELECT to populate the variable, using SET as you have done here is just fine and clearer IMO. In addition, you can be guaranteed that Count(*) will never return a negative value so you need only check whether it is greater than zero.
[update] -- Well, my own foolishness provides the answer to this one. As it turns out, I was deleting the records from myTable before running the select COUNT statement.
How did I do that and not notice? Glad you asked. I've been testing a sql unit testing platform (tsqlunit, if you're interested) and as part of one of the tests I ran a truncate table statement, then the above. After the unit test is over everything is rolled back, and records are back in myTable. That's why I got a record count outside of my tests.
Sorry everyone...thanks for your help.

Resources