Laden...

Firebird / FbCommand.ExecuteReader funzt net mehr

Erstellt von chriscolm vor 12 Jahren Letzter Beitrag vor 12 Jahren 4.205 Views
C
chriscolm Themenstarter:in
112 Beiträge seit 2009
vor 12 Jahren
Firebird / FbCommand.ExecuteReader funzt net mehr

verwendetes Datenbanksystem: Firebird 1.5
NET 3.5
VS Express 2010
FirebirdSql.Data.FirebirdClient.dll 2.5.1.0

Moin,

folgendes Problem, und ich könnte gerade echt kotzen, weil es bisher immer funktioniert hat:
Ich übertrage mittels eines kleinen Programmes Datensätze aus einem Firebird 1.5 Server auf einen Firebird 2.5 Server, wegen einiger Änderungen an der Tabellenstruktur mache ich das nicht per Backup und Restore.
Folgender Code soll ausgeführt werden:


int cnt = 0;
            FbDataReader reader = null;
            using (FbConnection c = new FbConnection(FbConnectionStrings[0]))
            using (FbCommand cmd = c.CreateCommand()) {
                cmd.CommandText = "SELECT COUNT(*) FROM " + table;
                cmd.CommandTimeout = 60;
                try {
                    c.Open();
                    object o = 0;
                    reader = cmd.ExecuteReader();
                    while (reader.Read()) {
                        o = reader.GetValue(0);
                    }
                    cnt = Convert.ToInt32(o);
                } catch (Exception ex) {
                    cnt = 0;
                    Log(ex);
                } finally {
                    if (reader != null) {
                        reader.Close();
                    }
                }
            }

Soweit ich das überblicken kann, korrekt und in anderen Konstellationen (SQL Server, MySql etc) auch schon 1000 mal so korrekt gewesen. Verbindung kann geöffnet werden, sobald die Zeile reader.Read() ausgeführt wird, geht irgendwie alles sang und klanglos ins Nirvana. Mein Programm laäft weiter, als wenn nichts wäre, ich bekomme keine Fehlermeldung, es wird weder die folgende Zeile noch die catch-Klausel erreicht. Probiere ich das mit einem UPDATE-Befehl und cmd.ExecuteNonQuery() ist alles gut. Auch kann ich an anderer Stelle Datensätze lesen, bis beim Lesen die ersten 500 Datensätze einer Tabelle übersprungen werden sollen. Dann passiert das Gleiche wie eben beschrieben.
Ich kann mit FlameRobin alle mit ADO.NET scheiternden Befehle ausführen. Also Datenbank scheint in Ordnung zu sein, der Datenbankserver ist erreichbar und ich weiß irgendwie nicht so recht weiter. Falls jemand eine Idee hat, bin ich dabkbar. Grüße
Christian

F
10.010 Beiträge seit 2004
vor 12 Jahren
  1. Ein Count(*) kann keine Indizes benutzen, weshalb der Server alle Datensätze lesen muss. Da macht es sich dann bezahlt, wenn man Namenskonventionen auch für die DB hat, dann kann man z.b. immer Count(ID) oder Count(TableNameID) machen.

  2. Einzelne Werte mit einem DataReader auszulesen ist Blödsinn, dafür gibt es Execute Scalar.

  3. Die ersten 500 Datensätze einer DB gibt es nicht, nur die ersten 500 Treffer einer Abfrage, was einen grossen unterschied darstellt.
    Wie machst du denn das Überspringen?

  4. Über was für Datenmengen reden wir hier?

C
chriscolm Themenstarter:in
112 Beiträge seit 2009
vor 12 Jahren
  1. ist erstmal relativ egal, es geht erstmal ums Prinzip. Außerdem gibt es da unterschiedliche Meinungen, mag auch vom DBMS abhängen.
  2. ExecuteScalar hat nicht funktioniert, deswegen probierte ich den DataReader
  3. Firebird hat die Möglichkeit: SELECT FIRST SKIP 400 ...
  4. Es scheitert bereits an einer Tabelle mit 1300 Datensätzen.

Wie gesagt, bisher hat es immer zuverlässig funktioniert, auch bei Tabellen mit mehreren 100.000 Datensätzen. Ich habe vorhin mal folgendes probiert:
Im Programm das SELECT-Statement angepasst: SELECT * FROM TABELLE WHERE id ≥ 0 AND id < 100
dann jeweils um 100 erhöht
SELECT * FROM TABELLE WHERE id ≥ 100 AND < 200
usw.
Nach 400 Datensätzen hat sich wieder alles sang und klanglos verabschiedet. Ich vermute ja fast, dass die Datenbank selber irgendwo beschädigt ist, nur kann ich mit FlameRobin alles auslesen. Ist aber nicht ADO.NET.
Naja, mal sehen.

F
10.010 Beiträge seit 2004
vor 12 Jahren

ist erstmal relativ egal, es geht erstmal ums Prinzip. Außerdem gibt es da unterschiedliche Meinungen, mag auch vom DBMS abhängen.

Und nein, es ist nicht egal, diese Aussage machen sonst nur VB6 entwickler die keine Lust haben es richtig zu machen.
Und zeig mir einen vernünftigen DBA/Entwickler der diese Aussage so unterstützt.

Und wenn ExecuteScalar nicht funktioniert machst Du etwas falsch.

B
112 Beiträge seit 2008
vor 12 Jahren

verwendetes Datenbanksystem: Firebird 1.5
NET 3.5
VS Express 2010
FirebirdSql.Data.FirebirdClient.dll 2.5.1.0

Moin,

folgendes Problem, und ich könnte gerade echt kotzen, weil es bisher immer funktioniert hat:
Ich übertrage mittels eines kleinen Programmes Datensätze aus einem Firebird 1.5 Server auf einen Firebird 2.5 Server, wegen einiger Änderungen an der Tabellenstruktur mache ich das nicht per Backup und Restore.

Was hat sich geändert zwischen dem letzten Funktionieren und dem ersten Nichtfunktionieren?

Bist Du sicher, dass der FirebirdClient 2.5.1 mit der alten Server-Version umgehen kann? (Ich kann das hier nicht ausprobieren, habe nur Firebird 2.5.)

Folgender Code soll ausgeführt werden:

  
int cnt = 0;  
            FbDataReader reader = null;  
            using (FbConnection c = new FbConnection(FbConnectionStrings[0]))  
            using (FbCommand cmd = c.CreateCommand()) {  
                cmd.CommandText = "SELECT COUNT(*) FROM " + table;  
                cmd.CommandTimeout = 60;  
                try {  
                    c.Open();  
                    object o = 0;  
                    reader = cmd.ExecuteReader();  
                    while (reader.Read()) {  
                        o = reader.GetValue(0);  
                    }  
                    cnt = Convert.ToInt32(o);  
                } catch (Exception ex) {  
                    cnt = 0;  
                    Log(ex);  
                } finally {  
                    if (reader != null) {  
                        reader.Close();  
                    }  
                }  
            }  
  

Soweit ich das überblicken kann, korrekt und in anderen Konstellationen (SQL Server, MySql etc) auch schon 1000 mal so korrekt gewesen. Verbindung kann geöffnet werden, sobald die Zeile reader.Read() ausgeführt wird, geht irgendwie alles sang und klanglos ins Nirvana. Mein Programm laäft weiter, als wenn nichts wäre, ich bekomme keine Fehlermeldung, es wird weder die folgende Zeile noch die catch-Klausel erreicht. Probiere ich das mit einem UPDATE-Befehl und cmd.ExecuteNonQuery() ist alles gut. Auch kann ich an anderer Stelle Datensätze lesen, bis beim Lesen die ersten 500 Datensätze einer Tabelle übersprungen werden sollen. Dann passiert das Gleiche wie eben beschrieben.

Das verstehe ich nicht. Weiterlaufen kann das Programm doch beim besten Willen nicht, wenn weder die nächste Zeile noch die catch-Klausel erreicht werden? Das hängt doch dann fest, oder nicht? Im übrigen, was ist für Dich die "folgende Zeile": die Anweisung innerhalb der While-Schleife oder die erste Anweisung danach?

Die "FIRST(m) SKIP(n)"-Klausel verwendest Du aber schon mit einer ORDER-BY-Klausel?

Da Du den Tabellennamen als Variable in die Abfrage einbaust, gehe ich davon aus, dass die Abfrage auf verschiedene Tabellen losgelassen wird. Ist das Problem bei allen dasselbe? Im gleichen Zusammenhang: Du hast Dich vergewissert, dass ein korrekter Tabellenname in der fertigen Abfrage steht?

Und ich würde unbedingt zu ExecuteScalar zurückkehren und so lange forschen, bis das funktioniert. Es ist die richtige Methode für einen Einzelwert; wenn und so lange das nicht klappt, ist definitiv der Wurm drin.

C
chriscolm Themenstarter:in
112 Beiträge seit 2009
vor 12 Jahren

Moin,
also der Wurm ist definitiv in der Datenbank, wie ein Analyse mit gfix ans Tageslicht brachte.
Zwei weitere Datenbanken ließen sich problemlos konvertieren, da sagte gfix aber auch, dass die Datenbank in Ordnung sei 😃
Auf zwei Dinge möchte ich noch kurz eingehen, die in den Antworten angesprochen wurden:
Der Ausführungsplan bei SELECT COUNT(*)... oder SELECT COUNT(pk_spalte)... ist der Gleiche, macht also keinen Unterschied. Es wird in beiden Fällen kein Index benutzt.
Wo ist das Problem bei SELECT first...skip.... ohne order by? Läuft ganz sauber durch.

Grüße

Christian

B
112 Beiträge seit 2008
vor 12 Jahren

Wo ist das Problem bei SELECT first...skip.... ohne order by? Läuft ganz sauber durch.

Das ja, aber Du hast keine Kontrolle darüber, welche Datensätze übergangen werden. Wenn sie mit einem automatisch vergebenen Primärschlüssel versehen sind, sieht es zwar aus, als würde SELECT ohne ORDER BY danach sortieren (also in Wirklichkeit nach dem Eingabezeitpunkt), aber m.W. gibt es da keine Garantie.

Und z.B. bei PostgreSQL sehe ich tatsächlich gelegentlich Sätze in einer Reihenfolge, die davon abweicht.

Ich bin nicht einmal ganz sicher, ob die Reihenfolge bei mehreren derartigen Abfragen immer gleich bleibt, ob also ein portionsweises Durcharbeiten der Sätze auf diese Weise garantiert korrekt funktioniert.

Das "Firebird Book" erklärt einfach lapidar "It does not make sense to use this construct with an unordered set".