1

I have a table with 10M+ rows and want to change the data type of one the columns from nvarchar(254) to decimal(7,2). What is the most efficient and effective query to make this change?

I have tried using ALTER in order to make this change, but get an error in SSMS

Error converting data type nvarchar to numeric.

I have also tried using CAST, but this results in errors as well. Admittedly, I'm not a DBA so I have struggled to understand the following:

  1. How to properly write a CAST query that does not yield errors

  2. Whether the CAST and CONVERT functions change the design of the data at the database level (meaning in the Object Explorer, when I right-click the table and then click 'Design' I see the data type of the column has changed) or if the changes only last until the next query is run or the program is exited.

This table was initially created over a month ago as the result of a workflow that was run a few months ago; this workflow has since been scheduled to push new data to the table on an hourly cadence, so deleting the job/table and starting over is not an option.

SET STATISTICS TIME ON

ALTER TABLE Clone3

ALTER COLUMN Price decimal(7,2)

The ultimate goal is to store this data correctly so that arithmetic operations can be performed when it is ingested into other visualization programs (e.g., Tableau, Power BI, etc.) That said, the expected result here is for the data type to be changed to Decimal(7,2) but the actual result is nvarchar(254).

UPDATE

After running SELECT Price from Clone3 WHERE TRY_CONVERT(decimal(7,2),Price) IS NULLthere are 239 records that return in scientific notation. For example -5.0000000000000003E-2

FINAL UPDATE

I ran the following query to update the records that were causing the conversion error (these were negative numbers like '-0.05' being converted to scientific notation for some strange reason).

UPDATE Clone3 SET Price = CAST(Price AS Float) WHERE TRY_CONVERT(decimal(7,2), Price) IS NULL

Because all of the records are now in a numeric data type, I can convert the entire dataset to decimal(7,2), using this query.

ALTER TABLE Clone3 ALTER COLUMN Price decimal(7,2)

I think I can call this solved, so many thanks to everyone for their responses, especially @Larnu for the code snippet that eventually helped me figure this out.

dallen93
  • 11
  • 3
  • If you're getting an error about the conversion, you first need to find the offending values. Can you share (some of) the results from `SELECT Price from Clone3 WHERE TRY_CONVERT(decimal(7,2),Price) IS NULL;` Thanks. – Larnu Jan 21 '19 at 19:00
  • Also, I just noticed, but what do you mean by *"data type to be changed to `Decimal(7,2)` but the actual result is `nvarchar(254)`"*? If you want the result returned to be a `varchar` then store a `varchar`. Honestly though, that statement sounds like an [XY Problem](http://xyproblem.info). – Larnu Jan 21 '19 at 19:06
  • @Larnu - I tried to follow the guidelines presented while writing the post and think that I misread something. "Actual result" in my scenario is what I am currently seeing. To restate what I wrote above, "...the expected result here is for the data type to be changed to Decimal(7,2), but what I'm seeing currently is nvarchar(254)." Apologies for any confusion. – dallen93 Jan 21 '19 at 19:11
  • @Larnu - Here are a few of the results from the query you requested I run: ` -7.0000000000000007E-2 -5.0000000000000003E-2 -5.0000000000000003E-2 5.9999999999999998E-2 5.9999999999999998E-2 2.9999999999999999E-2 -5.0000000000000003E-2 2.9999999999999999E-2 -4.0000000000000001E-2 4.0000000000000001E-2 ` – dallen93 Jan 21 '19 at 19:13
  • Comments are not the place for such information. Please edit your post. – Larnu Jan 21 '19 at 19:55
  • @Larnu - Apologies again. Please let me know if the edit to my original post is appropriate/meets expectations. – dallen93 Jan 21 '19 at 20:08
  • Nothing to do with Alteryx, why tag it like that ? – cmcau Feb 08 '19 at 03:51
  • @cmcau - I did not explicitly state Alteryx, but I did indirectly refer to it here: "This table was initially created over a month ago as the result of a workflow that was run a few months ago; this workflow has since been scheduled to push new data to the table on an hourly cadence, so deleting the job/table and starting over is not an option." – dallen93 Feb 12 '19 at 14:47
  • There's an Alteryx tag on the post, might be best to remove that if this isn't an Alteryx problem at all :) – cmcau Feb 12 '19 at 22:51

2 Answers2

3

This 5.9999999999999998E-2 cannot be converted directly to decimal(7,2), although it can be converted to a float, which can then be converted to a decimal(7,2). EG

select cast(cast('5.9999999999999998E-2' as float) as decimal(7,2))

While not the most efficient, or a general solution for this kind of thing, you could alter the table twice, eg:

use tempdb

drop table if exists t
create table t(s varchar(200))
insert into t(s) values ('5.9999999999999998E-2')

go

alter table t alter column s float
alter table t alter column s decimal(7,2)

go
select * from t
David Browne - Microsoft
  • 50,175
  • 5
  • 27
  • 51
  • This sounds like a great idea, David. How would I do this dynamically to cast the results of `SELECT Price from Clone3 WHERE TRY_CONVERT(decimal(7,2),Price) IS NULL;` as a float? That query only yielded 239 results, so my thought is that after changing those datatypes to float, I can run the other part of your nested cast statement on the entire dataset. Thoughts? – dallen93 Jan 21 '19 at 19:42
0

The most efficient way is probably to empty the table and reload it:

select *
into temp_t
from t;

truncate table temp_t;

alter table t alter column price decimal(7, 2);

insert into t
    select *
    from temp_t;

There is more overhead to updating the records in place.

Gordon Linoff
  • 1,122,135
  • 50
  • 484
  • 624
  • 1
    That won't solve the error *"Error converting data type nvarchar to numeric."* though. – Larnu Jan 21 '19 at 19:00
  • Thanks for the response Gordon! Do you have any idea how long it would take to copy the table (again 10M+ records and counting with each hour)? Also, what is the purpose of the 'truncate table' statement? – dallen93 Jan 21 '19 at 19:04
  • 1
    There is no need to copy the rows around twice. Inserting to a new table with the desired structure and dropping and renaming would be more efficient if going this route – Martin Smith Jan 21 '19 at 19:06
  • @MartinSmith . . . The re-insert preserves indexes, triggers, and constraints. – Gordon Linoff Jan 22 '19 at 00:11
  • They can easily be added to the empty table to save having to copy all the rows twice (if you would even want the trigger to fire for this migratory insert anyway) – Martin Smith Jan 22 '19 at 00:12