2010-09-08 3 views
7

Stellen Sie sich eine Tabelle mit hundert verschiedenen Spalten vor. Stellen Sie sich vor, dass ich eine Benutzerdatentabelle habe, von der ich Daten in die Basistabelle kopieren möchte. Also habe ich diese einfache insert-select-Anweisung geschrieben und dieser Fehler taucht auf. Also, was ist der eleganteste Weg herauszufinden, welche Spalte den Fehler verursacht?Wie kann man herausfinden, welche Spalte beim Einfügen einen arithmetischen Überlauffehler verursacht?

Meine ersten Gedanken über die Lösung sind darüber in einer Transaktion Umwickeln, die ich letztlich eine Art Wasserscheide Rollback und verwenden und Conquer Ansatz:

begin tran 

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

rollback tran 

Und dies nicht offensichtlich. So teilen wir die Säule in der Mitte gesetzt wie 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 

Und wenn es scheitert dann die fehlerhaften Spalt in der anderen Hälfte. Und wir setzen den Prozess fort, bis wir die lästige Säule finden.

Etwas eleganter als das?

Hinweis: Ich fand auch ein Beinahe-Duplikat dieser Frage, aber es beantwortet es kaum.

+0

Wenn Sie die Datentypen von UserTable mit denen von BaseTable übereinstimmen, sollte Ihr Einfügen kein Problem haben. Alles was danach benötigt wird, ist das Finden der anstößigen * UserTable * Spalte . –

+0

@Lieven Nun, UserTable ist nur das ... eine Benutzertabelle ohne Einschränkungen, weil die Daten darin von Excel oder Access oder whatnot sind. –

+0

Ich sympathisiere. Wenn dies regelmäßig erledigt werden muss, könnten Sie nicht ein Skript/eine gespeicherte Prozedur erstellen, die Ihre Eingaben überprüft? Ein Makro und eine einfache Auswahl würden sehr weit gehen, etwa "SELECT" c1 ', CAST (c1) AS INTEGER FROM UserTable'. –

Antwort

6

Nach Skript SELECT Anweisungen für jede Integer-Spalte von Basetable schaffen würde.
Das Ausführen der resultierenden SELECT Anweisungen sollte die störenden Spalten in Ihrem Usertable lokalisieren.

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' 
+0

Wow, ich mag diesen Ansatz. –

-1

Oft ist die Brute-Force-Methode, die Sie vorschlagen, der beste Weg.

Wenn Sie jedoch eine Kopie der Datenbank haben, können Sie auch gefälschte Daten posten.

führen Sie die Abfrage auf, damit Sie nicht über die Transkation die Spalte verstecken, die es bricht. Manchmal in dem Fehler wird es einen Hinweis geben, was los ist. Normalerweise, wenn Sie schauen, was hineingeht, können Sie sehen, wenn Text in ein int geht oder umgekehrt.

Ich mache dies und das nimmt alles andere in meinem Code, was das Problem verursacht.

Sie müssen eine Kopie der Abfrage erstellen, die Sie kopieren und in ein Abfragetool einfügen können.

+0

Ja, es heißt: Arithmetischer Überlauf in varbinary mit Wert 1239847234.000000. Oder etwas in diesem Sinne. Ich kann definitiv die Fehlermeldung lesen, aber das hilft mir in keiner Weise. –

+0

Sind also alle Werte, die Sie aus Varchar ziehen und alles, was Sie in Varchar einfügen? weil die Varbinär dazu neigen, mich denken zu lassen, dass das nicht wahr ist. –

+0

Spalten in BaseTable und UserTable sind sie die gleichen mit anderen Worten? –

-1

Ich denke, Sie sind der falsche Ansatz. Wenn Sie einen arithmetischen Überlauf erhalten, indem Sie einfach Spalten aus einer Tabelle auswählen und in eine andere einfügen, müssen Sie größere Kolumnen (z. B. bigint) auswählen und in kleine Spalten (z. B. int) einfügen. Dies ist grundsätzlich falsch, und Sie müssen Ihre DB-Struktur ändern, so dass das Einfügen von Zeilen von einer Tabelle in eine andere funktioniert. Überprüfen Sie jede Spalte von jeder Tabelle und sehen Sie, wo ein Überlauf möglich ist. Passen Sie dann die Zieltabelle an, damit die Daten, die Sie einfügen, passen.

Ich denke immer noch mein Punkt steht aber als Reaktion auf Ihre Kommentare, wenn Sie eine schnelle und schmutzige Lösung wollen. Machen Sie alle Ihre Spalten in BaseTable varchar (MAX).

Dann:

insert into BaseTable (c1,c2,...,cN) 
select CAST(c1 AS varchar(max)),CAST(c2 AS varchar(max))...,cN 
from UserTable 
+2

Nein, ich sehe keinen Fehler in meinem Ansatz. Ich habe keine Kontrolle über die UserTable und alle Spalten in dort sind Varchar. Ich möchte nur schnell und schmutzig herausfinden, welche Spalte einen Fehler verursacht. Es ist nur ein anfängliches Laden von Daten und sollte nicht viel Zeit brauchen, um dies oder das zu überprüfen. Du weißt was ich meine? –

+1

Und das beantwortet meine Frage nicht, das entzieht sich ihr. –

+0

Alle Spalten sind varchar? Alle von ihnen? –

0

Wenn dies ist nur etwas, das Sie manuell ausgeführt werden dann je nachdem, wie viele Daten Sie einfügen Sie die OUTPUT Klausel zur Ausgabe der eingefügten Zeilen an den Client nutzen könnten.

Die Zeile nach der letzten, die ausgegeben wird, sollte die mit dem Problem sein.

+0

Sagen wir, ich habe nur eine Zeile mit vielen Spalten. :) –

+0

Dann müssen Sie nur auf die eine Zeile schauen. – Sam

+2

@Sam, du verpasst den Punkt. Problematisch ist nicht die Anzahl der Zeilen, sondern die Anzahl der Spalten. –

0

Ich nahm Lieven Keersmaekers Ansatz, aber erweiterte es. Wenn eine Tabelle verschiedene numerische Feldlängen aufweist, ändert dieses Skript den Cast basierend auf dem Typnamen und der Genauigkeit. Kredit geht immer noch an Lieven, weil er über diese Lösung nachgedacht hat - das hat mir sehr geholfen.

DECLARE @tableName VARCHAR(100) 

SET @tableName = 'tableName' 

SELECT 'PRINT ''' + sc.NAME + '''; SELECT MIN(CAST([' + sc.NAME + '] as ' + CASE 
     WHEN st.NAME = 'int' 
      THEN 'int' 
     ELSE st.NAME + '(' + cast(sc.precision AS VARCHAR(5)) + ',' + cast(sc.scale AS VARCHAR(5)) + ')' 
     END + ')) from ' + @tableName 
FROM sys.columns sc 
INNER JOIN sys.types st ON st.system_type_id = sc.system_type_id 
WHERE OBJECT_NAME(Object_ID) = @tableName 
    AND st.NAME NOT IN ('nvarchar', 'varchar', 'image', 'datetime', 'smalldatetime', 'char', 'nchar') 
Verwandte Themen