C# Ein Ausdruck ist zu lang oder zu komplex für eine Kompilierung

roker002

Commander
Registriert
Dez. 2007
Beiträge
2.061
Ich habe einen statischen Query geschrieben das ungefähr 10.000 Zeilen in die Datenbank 1x Einfügen sollte. Die Query an sich ist korrekt.

Code:
Compiler Error
Ein Ausdruck ist zu lang oder zu komplex für eine Kompilierung.

Das Problem tauchte erst auf als ich den VS 2010 eingestzt habe. In 2008 gab es diese Meldung nicht. Woran kann es den liegen?
 
Wie wäre es denn wenn du den Ausdruck als Stored Procedure oder Stored Statement ablegst und einfach im Code mit Werten füllst. Lässt sich mit LINQ to Sql relativ gut bewerkstelligen.
 
Ich glaube es liegt nicht an am Sql Statement selbst, sondern an der Länge des Strings in der Klasse.

Ich kann ja sogar mit dem VS Editor das ganze garnicht öffnen! der 2010 stürzt ab! War übrigens vorher garnicht so schlimm.
 
ne der kommt mit dem IntelliSense nicht mehr klar! steht nur dass aufgrund der IntelliSense VS geschloßen werden muss.

Naja das Problem habe ich gelöst indem ich der riesigen Befehl in viele 1.000 zeilige zerschnitten hatte und dann mit dem String (getter) diese wieder zusammengefügt habe.

Ich denke dass es wirklich an dem IntelliSense lag. der VS 2008 hat da nicht gemekert... :D
 
Das VS2010 ist ohne ein Service Pack sowieso noch sehr verbuggt. Bin wieder gedowngradet, spästestens als ich bemerkt hab, dass das XNA nicht mit 2010 funktioniert
 
Anstatt hier das Visual Studio zu verteufeln, würde ich einmal schwer in Frage stellen, ob ein Statement, das über 10K Zeilen geht wirklich notwendig ist. Wieso fügst du die Daten nicht hintereinander ein?
 
Du meinst einen ArrayList aus jeder Einzelnen INSERT Statement machen? Eigentlich macht es ehe kein Unterschied, ausser, VS stürzt vielleicht nicht mehr ab wenn man einen Array mit 10k Zeilen hat :D

Dieser Statement mit 10k Zeilen ist automatisch generiert aus mehreren Dateien. Es war ne teufelische Arbeit die Daten Sinnvoll zu sammel.
 
Hast du schon mal was von Prepared Statements gehört, dadurch wird dann sogar dein Programm schneller (und VS stürzt nicht ab).
Alle inserts in einer Datei abspeichern, dann Zeile für Zeile den Query ausführen
Code:
db->autocommit=false;
prep=db->prepare("insert into ... values(?,?,?)");
file f("datei");
while(string s=f.readline())
{
prep->BindParam(1,s);
prep->execute();
}
f.close();
db->commit();
db->autocommint=true;
Das ist als Pseudocode zu verstehen.
 
Ne es macht wirklich kein Unterschied weil es so ähnlich bei mir abläuft! Der String den ich übergebe ist einfach Extrem lang für den IntelliSense.

Es ist einfach so zu verstehen.... wenn ich jetzt db->prepare(SuperLangerString), ändert sich an der Laufzeit nicht. Eigentlich läuft das Programm eingentlich sehr schnell als wenn man eine Schleife machen würde und jeden Insert einzel eingeben müsste.
 
Da hast du was falsch verstanden.
Beim Prepare prepariert man ein Statement, also Konkret:
"insert into table values(?)"
ist relativ kurz.
Nun wird das ? an eine Variable gebunden
bindParam(1,laaaaangerstring)
Dadurch musste der SQL-Compiler nur "insert into table values(?)" bearbeiten, der Rest kann schnell durchgereicht werden.
Wenn du jetzt 10k inserts hast, dann muss deine Datenbank nur 10k strings einfügen, dabei aber nur einmal das Statement kompilieren, was schön kurz ist. Mit Autocommit=false wird das ganze dann sogar gepuffert -> Speeed (40MB/s und 10k inserts/s zusammen sind schon drin).
 
Aso meinst du das... ist klar dass es schneller ist. Muss mir aber doch die ganzen Variable separat abspeichern. Hm klar nach dem kompilieren wird die Datei nicht so aufgeblasen und verbraucht nicht so viel Arbeitsspeicher, aber man hat ja dann Problem mit den Laufzeiten. Eine Große Query auszuführen ist Schneller als 10k kleinere durch eine Schleife wandern zu lassen. Auf 5 MB Arbeitsspeicher kann man ja auch so verzichten, heute ist das eine Verschmerzbare grenze.

Hm aber kannst du vielleicht einen Beispiel angeben? Kein Pseudo Code. C# oder C++
 
PHP?
In C++ hab ich normalerweise kein DB-Zugriffsachen :) .
PHP ist mit PDO ja sehr wie C++:
Code:
$db=new PDO("mysql:/mydb","root","geheim");
$prep=$db->prepare("insert into table values(?)");
$db->beginTransaction();//AutoCommit->off
$file=file_get_contents("File");
$file=split("\n",$file);
for($i=0;$i<count($file);$i++)
{
$prep->bindParam(1,$file[$i]);
$prep->execute();
}
$db->commit();
Das sollte Funktionieren, die C++-Bibliotheken müssten ähnliche Funktionen bereitstellen.
Anmerkung:
in php nimmt bindParam eine Referenz, das heißt, einmal binden und nach jeder Änderung der Variable muss nur noch execute() aufgerufen werden.
 
1.) Prepared Statements sind etwas, wenn es wirklich um das letzte Promille an Performance geht bzw. wenn man sich gegen SQL Injections absichern will. In der Realität ist es von der Performance ziemlich egal, da das Verarbeiten der Statements sowieso relativ flott geht und der eigentliche Aufwand die Aktualisierung der Indizes beim Einfügen ist. Wenn man eine neue Tabelle anlegt und gleich befüllt, so ist es ratsam die Indizes erst nach dem Einfüge anzulegen natürlich vorausgesetzt, dass schon sichergestellt ist, dass alle Unique Einschränkungen geprüft sind.

2.) Wie das mit den Prepared Statements und Datenbankzugriff funktioniert, hängt nicht von der Sprache, sondern von der Bibliothek ab. PHP, C++, Java und .NET sind hier komplett unterschiedlich. Da hilft Beispielcode aus einer fremden Bibliothek nicht viel.
 
so wie ich es verstanden habe wird man bei .NET SqlParameter nutzen oder?

Ich verstehe nicht wieso es keinen unterschied ausmacht wenn ich eine Schleife 10.000 x mache oder nur eine Sehr große Query ausführe. Nach der Berechnung der Laufzeiten wird doch die Schleife verlieren oder irre ich mich da?
Code:
System.Data.SqlClient.SqlCommand cmd = null;
int result = 0;
try
{
    cmd = DataBase.SQLCommand(query, DataBase.Connect(true));
    foreach (String s in parameters.Keys)
    {
        Object[] t = (Object[])parameters[s];
        int len = t.Length;
        if (len > 2)
        {
            if (t[0].Equals("NULL") || t[0].Equals(null) || t[0].Equals(String.Empty))
                t[0] = DBNull.Value;

            DataBase.Parameter(ref cmd, s, t[0], (DbType)t[1], (Boolean)t[2]); //hier füge ich die Parameter ein. Von mit selbst entwickelte Methode und nichts Standardes.
        }
        else
        {
            DataBase.Parameter(ref cmd, s, t[0], (DbType)t[1]);
        }
    }

    result = DataBase.ExecuteNQ(ref cmd);
    cmd.Connection.Close();
}
catch (Exception e)
{
    ErrorLog.WriteError(e);
}
if (cmd != null)
    cmd.Dispose();
return result;

DBType ist klar.... und null ist boolean ob die Daten NULL sein dürften. also NULLABLE.

Sowas ist gemeint oder?
 
Da hast du Recht, wenn du keine Prepared Statements nutzt, ist die Methode mit dem Megastring schneller, wenn du Prepared Statements nutzt, ist die Methode mit der Schleife schneller. (SQL ist eine Sprache, die kompiliert wird, genauso wie C/C++ Java Pascal ..., je länger der Quelltext, desto langsamer die Compilierung -> Möglichst kurzes Statement oft wiederverwenden).
 
Performance ist die eine Sache, Stabilität die andere. Wenn es nicht unbedingt notwendig ist, würde ich trotzdem auf das Megastatement verzichten.
Bei einer Schleife kann man ziemlich einfach sagen, warum es nicht funktioniert hat, weil eventuell falsche Daten geliefert wurden. Bei dem Megastatement kann man sich nur in die Ecke setzen und heulen, weil in 10K Zeilen findet man den Fehler sicher nicht.
 

Ähnliche Themen

G
Antworten
6
Aufrufe
1.744
Zurück
Oben