views:

65

answers:

4

Imagine a table with like a hundred of different columns in it. Imagine, then, that I have a user-data table from where I want to copy data to the base table. So I wrote this simple insert-select statement and this error pops up. So, what's the most elegant way to figure out which column raises the error?

My initial thoughts on the solution are about wrapping it in a transaction that I will ultimately rollback and use a sort of Divide and Conquer approach:

begin tran

insert into BaseTable (c1,c2,c3,...,cN)
select c1,c2,c3,...,cN 
from UserTable

rollback tran

And this obviously fails. So we divide the column set in half like so:

begin tran

insert into BaseTable (c1,c2,c3,...,cK) --where K = N/2
select c1,c2,c3,...,cK --where K = N/2
from UserTable

rollback tran

And if it fails then the failing column is in the other half. And we continue the process, until we find the pesky column.

Anything more elegant than that?

Note: I also found a near-duplicate of this question but it barely answers it.

A: 

A lot of the times the brute force method you suggest is the best way.

However, if you have a copy of the database that you can post fake data too.

run the query on it so you don't have about the transcation hiding the column that is breaking it. Sometimes in the error it will give a hint as to what is up. Usually if you are looking at what is going in you can see when text is going into an int or vice versa.

I do this and that takes away anything else in my code causing the problem.

You need to get a copy of the query that is generated where you can copy and paste it into a query tool.

Nathan Stanford
Yeah, it says: Arithmetic overflow in varbinary with value 1239847234.000000. Or something to that effect. I definitely can read the error message but that doesn't help me in any way.
Denis Valeev
So are all the values you are pulling from varchar and all you are inserting into varchar? because the varbinary tend to make me think that this is not true.
Nathan Stanford
Columns in BaseTable and UserTable are they the same in other words?
Nathan Stanford
@Nathan_Stanford I think the value from UserTable is in varchar form and it's inserted in the BaseTable in the form of a smallint or tinyint or something like that. Anyways, that doesn't change anything. I want to know the column name that causes the problem and preferably fast.
Denis Valeev
A: 

I think you are taking the wrong approach. If you are getting an arithmetic overflow by simply selecting columns from one table and inserting into another then you must be selecting from bigger colimns (e.g. bigint) and inserting into small columns (e.g. int). This is fundamentally incorrect thing to be doing and you need to alter your DB structure so that inserting rows from one table into another will work. Inspect each column from each table and see where it is possible to get an overflow, then adjust your destination table so that the data you are inserting will fit.

I still think my point stands but in response to your comments if you want a quick and dirty solution. Make all your columns in BaseTable varchar(MAX).

Then:

insert into BaseTable (c1,c2,...,cN)
select CAST(c1 AS varchar(max)),CAST(c2 AS varchar(max))...,cN 
from UserTable
Ben Robinson
No, I don't see any flaw in my approach. I have no control over the UserTable and all columns in there are varchar. I want just a quick and dirty way to figure out which column causes an error. It's just an initial loading of data and should not take a lot of time of checking this or that. You know what I mean?
Denis Valeev
And that's not answering my question, that's evading it.
Denis Valeev
All Columns are varchar? All of them?
Nathan Stanford
@Nathan_Stanford Doesn't really matter. A linked server connected to Access or whatever!
Denis Valeev
But in the BaseTable they are not varchar, let's say that they all are varchar in the UserTable, to simplify things. Meaning that you don't need to cast them to varchar.
Denis Valeev
If they are not varchar(max) in base table make them so. If you want a table that you can safely insert data in without knowing the datatype of the columns then varchar(max) is your man. You can cast just about anything into a varchar(max).
Ben Robinson
+1  A: 

Following script would create SELECT statements for each integer column of Basetable.
Executing the resulting SELECT statements should pinpoint the offending columns in your Usertable.

SELECT  'PRINT ''' 
        + sc.Name 
        + '''; SELECT MIN(CAST(' 
        + sc.Name 
        + ' AS INTEGER)) FROM Usertable'
FROM    sys.columns sc 
        INNER JOIN sys.types st ON st.system_type_id = sc.system_type_id
WHERE   OBJECT_NAME(Object_ID) = 'BaseTable'
        AND st.name = 'INT'
Lieven
Wow, I like this approach.
Denis Valeev
+1  A: 

If this is just something you are running manually then depending upon how much data you are inserting you could use the OUTPUT clause to output the inserted rows to the client.

The row after the last one that is output should be the one with the problem.

Martin Smith
Let's say I have only one row with a lot of columns. :)
Denis Valeev
Then you only need to look at the one row.
Sam
@Sam, you're missing the point. It's not the amount of rows that is problematic but the amount of columns.
Lieven