Trying to convert NVARCHAR column value to DECIMAL(10,2) - sql-server

I am getting this error when I'm trying to convert all nvarchar values in a specific column in my table and remove the $ from the value as well.
Here is the error:
Msg 8114, Level 16, State 5, Line 7
Error converting data type nvarchar to numeric.
Here is my code:
UPDATE dbo.CMS_BI_Demand
SET DemandAmount = (SELECT CONVERT(DECIMAL(10, 2), REPLACE(DemandAmount, '$', '')) as DemandAmt
FROM dbo.CMS_BI_Demand
WHERE BISysID = 1)
Any help/direction would be appreciated.
Here is my code that corrected the error:
UPDATE dbo.CMS_BI_Demand
SET DemandAmount = t1.DemandAmount
FROM (
SELECT BISysID, CONVERT(DECIMAL(10,2), REPLACE(REPLACE(DemandAmount,'$',''),',','')) as DemandAmount
FROM dbo.CMS_BI_Demand
) t1
WHERE dbo.CMS_BI_Demand.BISysID = t1.BISysID;

Are you sure the values are all numeric after replacing the $? I ran into this once and had to do some data cleanup before I could do the update. In my case, there was a space in one of the rows so it failed. As soon as I fixed the record, everything worked.
You didn't specify what sql you're running but if you're using Microsoft SQL, I would run a check using isnumeric and verify that nothing returns as false:
SELECT id, ISNUMERIC(REPLACE(DemandAmount,'$','')) as DemandAmt
FROM dbo.CMS_BI_Demand
WHERE BISysID = 1 AND ISNUMERIC(REPLACE(DemandAmount,'$','')) = 0

Different cultures have different decimal and thousand separators. When you call CONVERT the server will use the locale specified through the LANGUAGE setting to try and parse the string. At best, if the string and locale don't match, you'd get an error. The worst case is that the string will be passed using the wrong separator.
Given the $ symbol, it looks like you're trying to parse a US-style numeric string in a non-US locale. The solution is to use PARSE or TRY_PARSE with en-us as the culture :
select parse('234.5' as numeric(10,2) using 'en-us')
You can avoid trimming the currency sign if you parse to money first :
select parse('$234.5' as money using 'en-us')
DON'T try to replace separators. This can easily lead to wrong values and still fail to parse the text. What if the value was 2,345.123? Even if you remove the thousand separator, trying to convert this value eg in Germany would produce 2345123.00 :
select parse('2345.123' as decimal(10,2) using 'de-DE')
Returns :
2345123.00
You may assume this won't happen in production. How about the EUR/USD exchange rate? In Italy?
SELECT PARSE('1.12' as decimal(10,2) using 'it-IT')
------
112.00
Oops

To figure out what values your SQL SERVER instance can't properly cast to decimal(10,2), use this:
SELECT DemandAmount
FROM dbo.CMS_BI_Demand
WHERE TRY_CONVERT(DECIMAL(10,2), REPLACE(REPLACE(DemandAmount,'$',''),',','')) IS NULL

Related

In MS SQL an index on a computed column that uses RIGHT and CHARINDEX results in Invalid length parameter passed to RIGHT

I have this computed column
ALTER TABLE mytable
ADD vMessage AS (CONVERT([nvarchar] (200),RIGHT(Message,CHARINDEX('.',REVERSE(Message),1)-1),0))
And I am trying to make an index on vMessage
CREATE NONCLUSTERED INDEX [IX_vMessage]
ON mytable ([vMessageType])
I get this error
Invalid length parameter passed to the RIGHT function.
Creating the computed column and running this query works
SELECT
Message,
RIGHT(Message,charindex('.',reverse(Message),1)-1),
CONVERT([nvarchar](200),RIGHT(Message,CHARINDEX('.',REVERSE(Message),1)-1),0)
FROM mytable
The data is similar to this sample data.
DECLARE #mytable TABLE (message nvarchar(1024))
INSERT INTO #mytable (message) VALUES
('Services.Common.Contracts.InternalContract'),
('Services.Common.Contracts.ItemArchivedContract'),
('Services.Common.Contracts.ItemCreatedContract'),
('Services.Common.Contracts.ItemInformationUpdatedContract'),
('Services.Common.Contracts.EmailContract'),
('Services.Common.Contracts.Customer.SetCredentialsContract'),
('Services.Common.Contracts.InternalItemContract')
SELECT
Message,
RIGHT(Message,charindex('.',reverse(Message),1)-1),
CONVERT([nvarchar](200),RIGHT(Message,CHARINDEX('.',REVERSE(Message),1)-1),0)
FROM #myTable
No message is empty. All messages have at least one dot character in them. Below returns nothing.
SELECT * FROM mytable WHERE CHARINDEX('.', Message) = 0
The problem is that CHARINDEX can return 0 if it doesn't find the value being searched for. To avoid causing errors because of this, you should always put CHARINDEX into NULLIF to null out the 0
ALTER TABLE mytable
ADD vMessage AS (
CONVERT(nvarchar(200),
RIGHT(
Message,
NULLIF(
CHARINDEX(
'.',
REVERSE(Message)
),
0
) - 1
)
)
);
I say always, because using a WHERE doesn't always help, as SQL Server often rearranges expressions. NULLIF uses a CASE internally, which is the only guaranteed way for this not to happen.
The code starts by reversing the string and then using CHARINDEX() to look for a . character. Since you don't want to keep the . in the final result, you then subtract 1 from the returned value. This is where the problem comes in.
CHARINDEX() returns 0 if the value isn't found (using 1-based rather than 0-based indexing for the string). When we subtract 1 from that and pass it to the RIGHT() function, you have an invalid argument and will see this error.
But I also see this:
All messages have at least one dot character in them.
I suggest checking that again. Perhaps the test is running in a different environment from production. We can see your query runs fine on the provided sample data when we load it to db fiddle:
https://dbfiddle.uk/bUMPVALz

Conversion failed when converting date and/or time from character string during update only

I have been searching for a resolution for a long time now and just can't seem to formulate a query that brings back the resolution so as a last resort I have posted here.
I have a SQL server table with a varchar column that has the date and time stored in this format
"1/1/2013 11:38:31 PM Some other text"
I needed this date and time portion of this data to be stored in another column in datetime datatype. So I created a new column called DateTimeLog of type datetime.
I then used left to chop off the extra text and convert to change the value to datetime format and got the result I would expect.
select CONVERT(DATETIME,(rtrim(left(olddate, 21)))) from mytable
results:
"2013-01-01 23:38:31.000"
So far, so good. this is what I would expect. My troubles begin when I attempt to update my new datetime column with the results of this CONVERT statement.
update mytable
SET DateTimeLog = CONVERT(DATETIME,(rtrim(left(olddate, 21)))) from mytable
I get the infamous "Conversion failed when converting date and/or time from character string" error message.
Conversion failed when converting date and/or time from character string.
I have also attempted to use cast
update mytable
SET DateTimeLog = (cast(CONVERT(DATETIME,(rtrim(left(oldtable, 21)))) as datetime)) from mytable
the error persists. As best I can tell the convert is working correctly because I can see the result set from a select, but getting that result into a new column has eluded me thus far.
thanks,
Your string isn't going to consistently be 21 characters long. Your sample data shows a single character month and a single character date. What if it's, say, 12/13/2018?
That said, you need a more robust way to isolate that timestamp. I used a PATINDEX to capture the position of the last colon in the time component, with a couple of regexes in there to account for the numbers & the AM/PM thing. Then I added 6 to it to get to the end of the string of interest.
This seems to work:
DECLARE #t TABLE (olddate VARCHAR(100));
INSERT #t
(
olddate
)
VALUES
('12/13/2018 11:38:31 PM Some other text')
,('1/1/2018 11:38:31 PM Some other text');
SELECT CAST(LEFT(olddate,PATINDEX('%:[0-9][0-9] [AP]M%', olddate)+6) AS DATETIME)
FROM #t;
Results:
+-------------------------+
| 2018-12-13 23:38:31.000 |
| 2018-01-01 23:38:31.000 |
+-------------------------+
Rextester: https://rextester.com/BBPO51381 (although the date format's a little funky on the output).

SQL Server Converting varchar to datetime

I got a problem in SQL Server with converting a varchar to datetime. I would like to convert/update whole column [datelog] in table:
[dd.mm.yyyy hh:mm:ss]` to `[yyyy-mm-dd hh:mm:ss]
In SQL Server 2012+ you can use PARSE or TRY_PARSE to parse a text value according to a specific culture.
Assuming your text follows the German culture ('de-DE') you can parse it to datetime with :
select PARSE('24.11.2015 13:10:55' as datetime using 'de-DE')
eg:
select PARSE(datelog as datetime using 'de-DE')
The real solution though would be to use the correct field type, ie datetime. It's almost guaranteed that someone, somewhere will either enter text with the wrong format or try to convert the text using the wrong culture.
Date types on the other hand, have no format, they are simply binary values. Using them is faster, safer and easier.
Tricky solution,
DECLARE #inputDate AS VARCHAR(20)='21.11.2015 06:59:00' -- [dd.mm.yyyy hh:mm:ss]
SET #inputDate = REPLACE(#inputDate ,'.' ,'/')
SELECT CONVERT(VARCHAR(24) ,CONVERT(DATETIME ,#inputDate ,103) ,121) OutputDate -- [yyyy-mm-dd hh:mm:ss]
Still you need to change as per your table columns.
temp table with one column of type varchar
create table #temp3 (someDate varchar (30))
insert into #temp3 values ('23.03.1989 15:23:43')
using a combination of concat, substring and right
select concat
(
SUBSTRING(someDate,7,4),'-', SUBSTRING(someDate,4,2),'-',SUBSTRING(someDate,1,2), ' ', right(someDate, 8)
)
from #temp3
gives: 1989-03-23 15:23:43

Convert Time zonoffset value to varchar

Query:
DECLARE #TimeZoneOffset datetimeoffset
SELECT #TimeZoneOffset = Time_Zone_Offset
FROM OFFSET_TABLE WHERE Active=1
Time_Zone_Offset column contains value like -6:00 (only offset)
When I do SELECT #TimeZoneOffset it throws me an error
Conversion failed when converting date and/or time from character string.
I know I am doing something wrong. I may need to CONVERT/CAST but can't get o/p so far.
Any help
To visualize what is happening here, try this:
DECLARE #x VARCHAR;
SET #x = 'abcdefghijklmnop';
SELECT #x;
Result:
----------
a
You have silently lost data from your variable, because you didn't bother declaring a length for your VARCHAR. In your case, I think you are ending up trying to use the string - somewhere, as opposed to the string -6:00.
I'm not sure how a simple SELECT yielded the error you mentioned; I suspect you are using it in some other context you haven't shown. But please try it again once your variable has been declared correctly.
Now I see why, your question wasn't correct - you said you were converting to VARCHAR but you weren't. This is not really unexpected, as -6:00 is not a valid DATETIMEOFFSET value; there is expected to be date and time components as well, otherwise the data type would just be called OFFSET. A valid DATETIMEOFFSET, according to the documentation, is:
DECLARE #d DATETIMEOFFSET = '1998-09-20 7:45:50.71345 -05:00';
So perhaps you have some datetime value and you want to apply the offset, well you can use SWITCHOFFSET() for that. However -6:00 is not a valid value; it needs to be in [+/-]hh:mm format (notice the leading 0 above, which seems to be missing from your sample data). So this would be valid:
DECLARE #datetime DATETIME = GETDATE(), #offset VARCHAR(6) = '-06:00';
SELECT SWITCHOFFSET(#datetime, #offset);
You need to correct the data in your offsets table and you need to change the way you are using the output. Personally, I've found it easier to stay away from DATETIMEOFFSET and SWITCHOFFSET(), especially since they are not DST-aware. I've had much better luck using a calendar table for offsets, storing the offsets in minutes, and using DATEADD to switch between time zones. YMMV.

Allow Search for Invalid Date stored as string - Is it recommended or not

I have two tables for an Entity - say Valid_Doc and Invalid_Doc. If document is valid, then all the data gets saved in Valid_Doc table.In case any of the attribute of document is invalid , it gets saved in Invalid_Doc.Due_Date is on of the column of both the tables. In Invalid_Doc , we are saving Invalid dates as string.
Suppose if user searches for documents through a SEARCH screen with following date
Due_Date - is after - 07/07/11,
Shall we show all the documents from both the tables.As Due_Date in Invalid_Doc table is string, there is no way we can compare the entered search date with the dates available in database in Invalid_Doc table.
Can someone please guide me whether to use DATEDIFF - i.e. need to convert the String date in DB to Date(millisecs) first and then do the comparison with the entered data.Doing this , there may be unpredictable results. So , shall we allow the user to search for Invalid doc through Date or NOT.
Select * FROM invalid_doc iil WITH (nolock) WHERE
CAST(Datediff(s, '19700101 05:00:00:000', iil.due_date) AS NUMERIC) *
1000
BETWEEN '1120501800000' AND '1120501800000'
Where '1120501800000' and '1120501800000' are Date converted in milliseconds.
Please suggest.
I would convert the dates in the database to a uniform string format using regex (eg. Q20011231Q) and also convert the query to the same format before searching.
This way you would have control on your data and can easily do comparisons
I continue as answer :)
As I don't know your language you handle the xml data I strongly suggest you validate you date befor you insert into your database.
It is not possible to enter a non valid data value into a datatime field.
if you have a data like 11/24/2011 and insert it, the datetime value always add the time itself.
Then you have eg. 11/24/20011 17:29:00 000 as value stored.
If you insert less then a date it might crash or if the value can be converted to a valid date, the missing parts will be replaced by "current date information".
So in fact you have to validate you string, convert it. Something like this:
-- #datestring <= somehow your string from xml
SET arithabort arith_overflow OFF
SET #date = CAST(#datestring AS DATETIME)
IF #date is NULL SET #date = GETDATE()
SET arithabort arith_overflow ON
You turn overflow error mode off, try convert and set default if it fails.
Or in MS SQL
IF ( ISDATE(#date_string) = 0 ) SET #date = GETDATE()
Hope this helps

Resources