BULK INSERT auf MSSQL - Fehler 4864



  • Hallo,
    ich möchte mittels BULK INSERT Daten aus einer CSV-Datei in meine DB importieren. Hier erst einmal der Aufbau der Tabelle:

    ID -> bigint - notnull - autoincrement
    Zeitstempel - datetime
    Wert - float
    Einheit - nvarchar(5)
    Kurztext - nvarchar(255)

    Die CSV-Datei hat nun Zeilen mit folgendem Inhalt:

    01.01.2017 01:00:00;90,444;Nm;V00119004231V 
    

    Ich verwende hier folgende Syntax:

    BULK INSERT [DATENBANK].[dbo].[TABELLE]
    FROM 'E:\tmp\testfile.csv'
    WITH (
        FIRSTROW = 2,
    	FIELDTERMINATOR = ';',
        ROWTERMINATOR='\r\n',
        Lastrow=1000 
    );
    

    Der Syntaxcheck sagt "alles ok". Wenn ich diesen dann ausführe, erhalte ich diesen Fehler:

    Msg 4864, Level 16, State 1, Line 1
    Bulk load data conversion error (type mismatch or invalid character for the specified codepage) for row 2, column 1 (ID).
    

    Ich verstehe das nicht ganz! Wenn ich das hier richtig lese, hat er ein Problem mit dem Wert für die erste Spalte(ID). Aber diese soll doch beim Anlegen neuer Datensätze automatisch incrementiert werden.
    Wenn ich einen DS manuell in der DB anlege (ohne Angabe der ID), macht er das und setzt die ID automatisch.

    Was kann ich hier tun???

    Viele Grüße
    Torsten



  • @torsten_156 sagte in BULK INSERT auf MSSQL - Fehler 4864:

    Fehlt da evtl. ein Format='CSV' in der WITH-Klausel?



  • ...werde ich mal testen. Kann allerdings etwas dauern 😞



  • Bulk insert erwartet auch einen wert für die ID column (steht so auch in der Fehlermeldung)

    Hab das hier gefunden
    https://stackoverflow.com/questions/10851065/bulk-insert-with-identity-auto-increment-column

    bzw. das hier:
    https://sqlstudies.com/2018/12/12/bulk-insert-and-an-identity-column/



  • Hallo,
    ich habe es nun geschafft direkt auf dem SQL-Server ein BULK INSERT Statement erfolgreich abzusetzen. Das Problem hier war tatsächlich die erste ID-Spalte. Ich habe in meinem File ein Trennzeichen (;) vorangestellt. Und jetzt geht zumindest der manuelle BULK INSERT.

    Da ich allerdings 900! Dateien verarbeiten muss, habe ich mir nun ein kleines Tool geschrieben, dass mir die jeweiligen Dateien in einer FileListBox angibt. Diese lese ich nun zeilenweise aus und übergebe den Dateinamen inkl. Pfad als Parameter an meinen SQL-String:

    AnsiString SQL ="BULK INSERT [EDIS_Energiedaten].[dbo].[tbl_EDIS_Daten_2] FROM :p01 WITH (FIRSTROW = 1, FIELDTERMINATOR = ';', ROWTERMINATOR='\n');";
    
    			try
    			{
    				//****************************
    				// Datensatz neu anlegen
    				//****************************
    
    				Q_Insert->Close();
    				Q_Insert->SQL->Clear();
    				Q_Insert->SQL->Add(SQL);
    				Q_Insert->Parameters->ParamByName("p01")->Value = myFile;
    				Q_Insert->ExecSQL();
    
    			} catch (const Exception& e)
    			{
    				Application->MessageBox(e.Message.c_str(), _D("Error"), MB_OK);
    			}
    

    Dabei bekomme ich jetzt folgenden Fehler:

    Falsche Syntax in der Nähe von '@P1' aufgetreten.
    

    Der Parameter ist als 'ftString' definiert.
    Ich checke nicht wo nun das Problem ist!

    VG
    Torsten



  • Habe die Lösung gefunden! Ich habe den Parameter aus dem SQL-Statement entfernt und den String wie folgt zusammen gebaut:

    "BULK INSERT....FROM " + FileName + " WITH (FIRST...";
    

    So hat es funktioniert 🙂

    VG
    Torsten



  • @torsten_156 sagte in BULK INSERT auf MSSQL - Fehler 4864:

    "BULK INSERT....FROM " + FileName + " WITH (FIRST...";
    

    Bobby Tables würde vielleicht noch ein wenig Zeit investieren herauszufinden, warum das mit dem ParamByName nicht funktioniert hat. Auch wenn das möglicherweise keine kritische Anwendung ist, gewöhnt man sich das besser nicht an, nur weil's ne schnelle Lösung ist 😉



  • @Finnegan
    Parameter gehen in SQL nicht an allen Stellen. z.B. kann man für Table- oder Spaltennamen üblicherweise keine Parameter verwenden.
    Wie das beim "FROM" vom "BULK INSERT" ist weiss ich nicht. Aber nachdem er "Falsche Syntax" sagt, würde ich annehmen dass es dort auch nicht geht.

    Was man sich aber angewöhnen sollte ist die Strings an solchen Stellen passend zu escapen.



  • @hustbaer Was ist das eigentlich für ne API? Zuerst dachte ich das wäre irgendwas aus diesem Moloch von Qt wegen der ganzen Q's. Scheint mir aber eher eine C++-API für den SQL Server zu sein, oder? Da muss es doch irgendwas geben, um einen selbst einen nicht-Daten-String ausbruchssicher zu escapen (Bin da nicht so bewandert und finde auf die Schnelle nix. Beruflich vor Jahren Oracle-Zeug und privat eher Postgres/MySQL gemacht).



  • Das ist die VCL.
    Und Q_Insert ist vom Typ TQuery (also steht das Q wohl für Query).



  • @Finnegan Keine Ahnung was das für ne API ist.
    Ich weiss nur dass es nicht in jeder DB API Funktionen zum Escapen gibt.
    Bei ODBC, ADODB oder auch ADO .NET wäre mir z.B. nichts dergleichen bekannt.

    Ich denke auch dass sowas enthalten sein sollte, aber ich hab's noch nirgends gesehen.



  • @hustbaer Hab lange nichts mehr mit Datenbanken gemacht und hätte erwartet, dass es da mittlerweile für jeden für SQL-Injection anfälligen Kontext eine Standardlösung gibt. Das Problem existiert ja nicht erst seit gestern und gehört meines Wissens immer noch zu den Top Sicherheitsproblemen 😞

    Eine simple und sichere Lösung ist wohl, einfach sicherzustellen, dass die Anwendung stets den Dateinamen kontrolliert. Erlaubt man Anwendern z.B. eine beliebige Datei hochzuladen, die dan via BULK INSERT eingefügt wird, sollte eben nicht der vom Anwender vorgegebene Name verwendet werden, sondern stattdessen ein selbst generierter, temporärer Dateiname.

    Bei dynamischen Tabellen- und Spaltennamen kann man zum validieren wohl prüfen, ob die Datenbankobjekte überhaupt existieren (Interessant übrigens, dass man wohl auch eine Tabelle anlegen kann, die "Users; DROP TABLE dbo.Users;" heisst - da vergeht mir schon gleich die Lust, sowas korrekt und sicher selbst zu implementieren, besonders wenn da noch die Regeln für gültige Dateinamen mit reinspielen 😉 ).


Anmelden zum Antworten