Laden...

Type/mismatch bei update in VFP

Erstellt von romu2000 vor 18 Jahren Letzter Beitrag vor 18 Jahren 3.617 Views
R
romu2000 Themenstarter:in
291 Beiträge seit 2006
vor 18 Jahren
Type/mismatch bei update in VFP

Hallo zusammen,

nachdem ich jetzt schon ein wenig geübt habe, und der DB-Update in eine VFP -DB wunderbar klappt, stehe ich seit heute Mittag auf dem Schlauch.

Folgender Code steht :


strUpdateSelect = "UPDATE KDST SET Name1='" + name1TextBox.Text + "', Name2='" + name2TextBox.Text + "' WHERE kdnr=12345 ";
            string ConnString =@"Provider=VFPOLEDB.1;Data Source=C:\Dokumente und Einstellungen\romu2005\Desktop\DBFS.001";
            OleDbConnection Conn = new OleDbConnection(ConnString);
            Conn.Open();

            OleDbCommand TEST = new
            OleDbCommand(strUpdateSelect, Conn);
            TEST.ExecuteNonQuery();

so klappts hervorragend.

Jetzt möchte ich aber das mir folgendes geupdatet wird:

strUpdateSelect = "UPDATE KDST SET Name1='" + name1TextBox.Text + "', Name2='" + name2TextBox.Text + "' WHERE kdnr='" + kdnrTextBox.Text + "' ;";

hier bekomme ich immer die Fehlermeldung Operator/operand type mismatch.

habe schon mit allem rumprobiert, aber ohne erfolg...mal da ein ' hin mal da ein " hin, usw....

kein Erfolg.

Vielleicht kann mir von euch jemand behilflich sein...!?!?

achja, das feld KDNR liegt als numeric feld vor !!

Viele Grüße

ronny

95 Beiträge seit 2006
vor 18 Jahren

Original von romu2000
WHERE kdnr=:::

Hier übergibst du als kdnr einen String, oben aber eine Zahl.

Wenn zwei dasselbe tun, ist es noch lange nicht dasselbe
(Adelphi)

3.728 Beiträge seit 2005
vor 18 Jahren
SQL Injection

Man baut SQL Anweisungen nicht mittels String-Verkettung. Schon gar nicht aus ungeprüften Benutzerengaben von TextBoxen. So ist Deine Anwendung sehr anfällig für SQL Injection Attacken.

Stell Dir mal vor, jemand ist gemein und gibt in name1TextBox folgendes ein:

'; DELETE FROM KDST --

Deine Anwendung würde folgedes zur Datenbank schicken:

UPDATE KDST SET Name1=''; DELETE FROM KDST --', Name2='bla' WHERE kdnr=12345 ";

Dadurch würden alle Datensätze in Tabelle KDST gelöscht.

Deshalb solltest Du IMMER Paramter verwenden (Die können sich zudem auch positiv auf die Leistung auswirken). Anstatt alles mit String-Verkettung zusammenzubauen, baust Du einfach Platzhalter (Fragezeichen dienen bei OLEDB als Platzhalter) für Parameter ins SQL-Statement ein. Dem OleDbCommand-Objekt kannst Du über die Paramaters-Auflistung die Werte für die Parameter übergeben (Genaueres dazu steht in der Online-Hilfe oder in der MSDN Library).

R
romu2000 Themenstarter:in
291 Beiträge seit 2006
vor 18 Jahren
hmmm

hallo zusammen.

danke für eure antworten.
@garby - so funktioniert es auch nicht .-(

@ rainbird - ich habe mich schonmal schlau gemacht. werde es so versuchen wie du es gesagt hast.
ich habe nur die problematik, das ich ca. 50 textboxen usw. auf einer form habe. heisst das ich muss für jeden wert einen parameter anlegen ?

viele grüße

ronny

R
romu2000 Themenstarter:in
291 Beiträge seit 2006
vor 18 Jahren
nochmal

hallo nochmal .-)

habe jetzt ein wenig die msdn durchstöbert und mir folgenden code zusammengeschustert.

allerdings klappt das so auch nicht wirklich 😦

  strUpdateSelect = "UPDATE KDST SET Name1='" +       name1TextBox.Text + "', Name2='" + name2TextBox.Text + "' WHERE kdnr='"?Kdnr"';";
            string ConnString = @"Provider=VFPOLEDB.1;Data Source=C:\Dokumente und Einstellungen\rmueller\Desktop\DBFS.001";
            OleDbConnection Conn = new OleDbConnection(ConnString);
            Conn.Open();

            OleDbCommand TEST = new
            OleDbCommand(strUpdateSelect, Conn);
            TEST.Parameters.Add("?Kdnr", OleDbType.Numeric, 5, kdnrTextBox.Text);
            TEST.ExecuteNonQuery();

Fehlermeldung- Operator/operand type mismatch.

weiss evtl. noch jemand rat ?

viele grüße

ronny

3.728 Beiträge seit 2005
vor 18 Jahren

Dein strUpdateSelect ist falsch. Dort musst Du die Parameter-Platzhalter einsetzen, anstatt mit String-Verkettung zu arbeiten. Hier ein Beispiel:

UPDATE KDST SET Name1=?, Name2=? WHERE kdnr=?

Dann musst Du nur noch die drei Parameter ans Command übergeben (das hast Du ja schon ganz gut gemacht). Nur den Parameternamen bitte nicht angeben (also "?Kdnr" nicht schreiben, sondern ""), da OLEDB die Parameter der Reihenfolge nach (von links nach rechts) den Platzhaltern zuordnet.

P.S. Datenzugriffscode gehört NIE, NIE, NIEMALS direkt in ein Formular. Du solltest die "Geschäftslogik" in autonome Klassen auslagern. Das spart viel Zeit und erleichert die Wartung der Anwendung. Die selbe Geschäftslogik-Klasse (z.B. Kundenstamm-Verwaltung) kann von mehreren Formularen bzw. auch anderen Geschäftlogik-Klassen verwendet werden. Wenn Du von der "Spaghetti-Programmierung" weg und weniger graue Haare kriegen willst, findest Du hier im Forum jede Menge nützlüche Informatione nzum Thema Software-Architektur. Hier einige passende Suchbegriffe: Schichten, Datenzugriffschicht, Geschäftslogik, MVC

R
romu2000 Themenstarter:in
291 Beiträge seit 2006
vor 18 Jahren
Command contains unrecognized phrase/keyword.

danke für die rasche antwort.

ich werde mich sobald die updatefunktion funktioniert auf alle fälle mit dem thema geschäftslogik befassen. den begriff habe ich hier schon sehr oft gelesen.

bezüglich des codes von gerade eben , bekomme ich jetzt folgende Fehlermeldung.

Command contains unrecognized phrase/keyword.

hier der Code:


strUpdateSelect = "UPDATE KDST SET Name1=?Name1 , Name2=?Name2 WHERE Kdnr=10002";
            string ConnString = @"Provider=VFPOLEDB.1;Data Source=C:\Dokumente und Einstellungen\rmueller\Desktop\DBFS.001";
            OleDbConnection Conn = new OleDbConnection(ConnString);
            Conn.Open();

            OleDbCommand TEST = new
            OleDbCommand(strUpdateSelect, Conn);

            TEST.Parameters.Add("?Kdnr", OleDbType.Double);
            TEST.Parameters["?Kdnr"].Value = kdnrTextBox.Text;
            TEST.Parameters.Add("?Name1", OleDbType.Char);
            TEST.Parameters["?Name1"].Value = name1TextBox.Text;
            TEST.Parameters.Add("?Name2", OleDbType.Char);
            TEST.Parameters["?Name2"].Value = name2TextBox.Text;
            TEST.ExecuteNonQuery();

ich wollte mal sehen , ob er wenigstens die textfelder name1 und name2 updatet. aber keine chance- es tritt immer obigrer fehler auf.

kannst du mir helfen?

viele grüße

ronny

3.728 Beiträge seit 2005
vor 18 Jahren
Nur ?

Nur ?, nicht ?Name!

Ich habe Dir oben ein Beispiel gepostet.

R
romu2000 Themenstarter:in
291 Beiträge seit 2006
vor 18 Jahren
tausend dank

hat super funktioniert 🙂 Danke vielmals.

Jetzt habe ich mal eine Frage zum Aufbau meines Projektes:

Tabelle1 - mit Kundendaten wie name,strasse,plz,ort....usw.
Tabelle2 - mit Kontaktdaten wie wiedervorlagezeit,wiedervorlagedatum,bearbeiter usw.

In Access habe ich derzeit eine Lösung laufen, die jedem Bearbeiter SEINE Adresse aus dem Kundenstamm (spalte Bearbeiter) durch eine Abfrage (SELECT Adressen From Kundenstamm WHERE wiedervorlagedatum und wiedervorlagezit KLEINER WIE DAS JETZIGE DATUM) vorgibt.

Nach Abschluss des Gesprächs werden kontaktdaten eingegeben und dann durch einen Button eine neue Abfrage generiert, so dass die nächste Adresse aufgerufen wird, die am nächsten an der jetzigen Zeit liegt.

Das Problem bei der Sache war die enorme netzlast, da jedesmal 40000 kunden abgefragt wurden ....

Jetzt wäre meine Idee, dass ich beim Start des Programms eine Abfrage starte (mit Progressbar) in der alle dem zugehörigen Bearbeiter verfügbaren adressen in ein DATASET geladen werden.
Nur habe ich hier ein Verständnissproblem.
Wie kann ich innerhalb des geladenen DataSets Abfragen nach Wiedervorlagedatum generieren ????

Ich arbeite ohne DataGrid....nur mit Textboxen, Listboxen usw.

Über einen Lösungsvorschlag wäre ich sehr dankbar...

Viele Grüße

Ronny

3.728 Beiträge seit 2005
vor 18 Jahren
Sql

Wenn ich Dich richtig verstanden habe, möchtest Du eine Adresse aus dem Kundenstamm bezüglich eines Wiedervorlage-Datensatzes für einen bestimmten Sachbearbeiter abrufen.

Der Sachbearbeiter sollte bekannt sein (z.B. der aktuell angemeldete Windows-Benutzer am Client). Die Einschränkung mit dem Datum ist für eine Wiedervorlage auch okay. Warum Du aber 40.000 Datensätze irgendwie übers Netz schieben musst, versteh ich nicht.

Wenn ich alles Richtig verstanden habe, benötigst Du nur ein SQL-Abfrage, die ungefähr so aufgebaut ist:

SELECT adr.*,rem.RemindOn,rem.ResponsibleUser,rem.Note,rem.ReminderID
FROM Address adr
INNER JOIN Reminder rem ON adr.AddressID=rem.AddressID
WHERE rem.ResponsibleUser=? AND rem.RemindOn<=?

Eine andere Möglichkeit wäre, erst alle Schlüssel der Wiedervorlage-Datensätze des Sachbearbeiters abzurufen:

SELECT ReminderID,AddressID,Note,RemindOn
FROM Reminder
WHERE ResponsibleUser=? AND RemindOn<=?

Die Adresse kann z.B. bei Bedarf über folgendes Statement nachgeladen werden:

SELECT *
FROM Address
WHERE AddressID=?

Das mit den 40.000 beim Start in ein DataSet lesen, solltest Du lassen. Das ist ein sogenannter "Schlemiel der Maler-Algorithmus". Wenn Du wissen willst, warum Schlemiel nicht gut für Deine Anwendung ist, solltest Du folgenden kleinen Artikel lesen: http://german.joelonsoftware.com/Articles/BacktoBasics.html

R
romu2000 Themenstarter:in
291 Beiträge seit 2006
vor 18 Jahren
So ähnlich

Hallo Rainbird,

das mit den 40000 Adressen habe ich glaube falsch erklärt.

Insgesamt gibt es 40000 Kunden. Bisher habe ich wie gesagt eine Abfrage wie Du sie oben geschrieben hast.
Allerdings ist die Performance nicht die Beste, da die DB auf dem Server liegt, und ich das Gefühl habe, dass VFP (an das ich bei diesem Projekt eben gebunden bin) bei Datumsabfragen Performancemässig ein wenig nachlässt 😦.

Desweiteren (so wurde mir vom Hersteller des WAWI-Programms gesagt) arbeitet die VFP-DB aus "Historischen" Gründen ohne Primary Keys 😦.

Daher war ja meine Überlegung, jedem Sachbearbeiter seinen AdressenPool lokal in ein DataSET zu speichern (das sind ungefähr zwischen 3000-4000 Adressen). Die Updates werden dann nach jeder Adresse (bis die neue aufgerufen wird) in die DB auf dem Server geschrieben.
Wenn sowas funktionieren würde(ein DATASET erneut nach Wiedervorlagen abzufragen) wäre das doch eine enorme Performancesteigerung, da ich nicht jedesmal, bei jeder Wiedervorlagenabfrage, extra die VFP DB durchsuchen muss, sondern lediglich im DATASET suchen kann...

Oder verstehe ich was falsch ???

Viele Grüße

Ronny

3.728 Beiträge seit 2005
vor 18 Jahren
Indizies

Wenn Du die Möglichkeit hast, Indizes auf der VFP-Datenbank anzulegen, dürfe sich einiges an Leistung herausholen lassen. Indizes tun auch Programmcode nicht weh, der nicht davon ausgeht, dass welche da sind. Auf jeden Fall solltest Du für alle Kombinationen von Feldern, nach denen in WHERE-Klauseln gesucht wird, Indizes anlegen (falls noch keine bestehen).

Du willst also die 3000-4000 Adressen auf dem Client des jeweiligen Sachbearbeiters cachen. Das DataSet liegt dann im Hauptspeicher des Clients rum. Je mehr Adressen ein Sachbearbeiter in seinem Pool hat (3000 sind noch nicht so viel, aber es könnten auch mehr werden, oder?), desto mehr Hauptspeicher wird benötigt. Wenn der Hauptspeicher nicht reicht, lagert Windows natürlich auf die Festplatte aus (rödel, rödel, ...), was sich wiederum negativ auf die Leistung auswirkt. Auf jeden Fall Leistungstests druchführen.

Du kannst natürlich innerhlab von DataSets/DataTables per Select-Methode (Damit meine ich die C#-Methode nicht SQL) suchen und Suchergebnisse als DataRow[] zurückgeben lassen. Das geht natürlich nur mit Daten, die auch im DataSet enthalten sind (die also schon im Hauptspeicher rumliegen). Im Prinzip müsstest Du alle Daten eines Sachbearbeiters in einem großen DataSet cachen, damit das funktioniert. Das würde ich nicht tun.

Du könntest auch die Sachbearbeiter-Adressen in einem DataSet (oder auch nur eine DataTable, wenn es nur eine Tabelle ist) lokal vorhalten und zur Abfrage der Wiedervorlage einen Datenbankzugriff machen (z.B. SELECT * FROM Remider WHERE AddressID IN (12,56,566, .....)). Wenn es allerings keine Indizes und Primärschlüssel gibt, ist der Zugriff allgemein langsam. Was ist außerdem, wenn Adressen von einem anderen benutzer geändert werden? Das DataSet im Hauptspeicher kriegt davon nichts mit. Deshalb kann ich so ein clientseitiges Caching nicht empfehlen. Auf einem Anwendungsserver macht sich das viel besser. Vorrausgesetzt alls Änderungen werden über die Schnittstellen auf diesem Server ausgeführt. Da das WAWI-Programm natürlich direkt UPDATES, INSERTS und DELETES auf die Datenbank absetzt, hätte man auch wie das "Sind die Daten im Cache aktuell?"-Problem. Wie ich es auch drehe und wende, finde ich, dass das lange Vorhalten von Adressen mehr Probleme schafft, als es löst.

Man sollte an der Ursache eines Problems ansetzen und keine Workarounds um das Problem herumprogrammieren. Sonst ist das wie Schmerzmittel bei einem Bienenstich geben, aber den Stachel nicht herausziehen. "Historisch gewachsen" ist keine Ausrede, da andere System am Markt mit Primärschlüsseln und Indizes auf der Datenbank arbeiten. Die Hersteller von Business-Software sind meistens ziemlich arrogant und denken sie können machen was sie wollen und haben immer Recht. ich habe selber ähnliche Erfahrungen mit "gekaufter" Business Software, deshalb geht mir da so der Hut hoch.

R
romu2000 Themenstarter:in
291 Beiträge seit 2006
vor 18 Jahren
grübel

Hallo Rainbird,

danke für Deine Antwort.

Ich glaube ich lerne durch Dich erstmals, ein Programm richtig von Anfang an aufzubauen 🙂 Vielen Dank ...

Was denkst Denn Du, wäre die richtige Lösung???

-Keine Primärschlüssel möglich (dadurch relativ langsame abfragen)

  • Indizes sind in VFP gesetzt. (aber nicht als primary)
  • Die WAWI Tabellen sind alle auf VFP gesetzt.
  • Bei den Zusatztabellen (WV/USER/Documents/.... hätte ich freie Auswahl der DB (wobei mir aber hier im Forum geraten wurde, alles auf einer DB aufzubauen, was in diesem FALL eben VFP wäre).

Wie würdest denn Du dieses Programm angehen?

Viele Grüße

ROnny

R
romu2000 Themenstarter:in
291 Beiträge seit 2006
vor 18 Jahren
noch was eingefallen.

Hallo nochmal,

mir ist jetzt noch was eingefallen.

Generell werden die Stammdaten im WAWI Programm von niemanden (ausser dem eigenen Sachbearbeiter) verändert. Das heisst, das der Sachbearbeiter immer den aktuellen Stand im DataSET hätte (DataTable fällt aus , da später noch auf andere VFP Tabellen zugegriffen wird.)

Wie kann ich denn so eine Abfrage innerhalb eines Datasets erzeugen, stehe da total auf dem schlauch!!

Viele Grüße

ROnny

3.728 Beiträge seit 2005
vor 18 Jahren
Indizes

Sind auch wirklich Indizes für alle Such-Kombinationen gesetzt? Viele Leute machen folgenden Fehler:

Angenommen Du hast eine Tabelle Offer (enthält Angebote). Nach diesen Angeboten wird auf verschiedene Art und Weise gesucht. So könnten diese verschiedenen Such-SELECTs aussehen:

SELECT * FROM Offer WHERE OfferID=?
SELECT * FROM Offer WHERE CustomerID=? AND OfferDate>?
SELECT * FROM Offer WHERE CustomerID=? AND State=?
SELECT * FROM Offer WHERE CustomerID=? AND State=? AND OfferDate>?

Die besagten lieben Leute würden jetzt folgende Indizes für die Tabelle Offer anlegen (und würden denken, dass sie alles richtig gemacht hätten):

Index1: OfferID
Index2: CustomerID
Index3: OfferDate
Index4: State

Der erste ist OK, die anderen drei bringen garnichts. Wenn nach mehreren Feldern gesucht wird, müssen auch all diese Felder (und zwar genau in der Kombination) in einem Index stehen. Richtig wären also folgende Indizes:

Index1: OfferID
Index2: CustomerID, OfferDate
Index3: CustomerID, State
Index4: CustomerID, State, OfferDate

Vielleicht ist bei Deiner VFP-Datenbank ja schon alles richtig gemacht. Ich würde nur zur Sicherheit nochmal nachschauen.

Zu Deiner Frage nach dem Scuhen und Filtern innerhalb von Datasets/DataTables:

http://www.microsoft.com/germany/msdn/library/net/adonet/FilternundaggregierenmitADONET.mspx

Zu Deiner Architektur-Entscheidung:

Wenn Dein Programm eine spezielle Erweiterung für dieses eine WAWI-System ist und Du es im Falle eines Wechsels zu einem anderen WAWI-System nicht mitnehmen willst, dann mach alles in die vorhandene Datenbank und halte Dich auch sonst möglichst bei Look & Feel an das WAWI-System (Es soll sich ja für den Benutzer wie aus "einem Guss" anfühlen). Für Deine eigenen Tabellen kannst Du ja Primärschlüssel setzen.

Wenn Du das alte fette Tier nicht weiter aufblasen willst, sondern Deine zusätzliche Funktionalität (Wiedervorlage, Dokumente verknüpfen etc.) von diesem kauzigen WAWI-System (Es muss kauzig sein, sonst würde es nicht mit FoxPro arbeiten und würde nicht historisch bedingt ohne Primärschlüssel laufen müssen) entkoppelt haben willst, solltest Du Dich **nicht **in der selben Datenbank niederlassen. Du solltest dann aber noch mehr tun. Deine Software sollte mit dem WAWI-System nur über sauber definierte Schnittstellen kommunizieren. Der Vorteil dabei ist: Wenn Du Deine Software für ein weiteres WAWI-System verfügbar machen willst/musst, brauchst Du an Deinem Code sehr wenig bis gar nichts zu ändern. Außerdem ist eine Kundenadresse immer eine Kundenadresse. Ob sie von kauzigen VFP-WAWIs kommt, oder von SAP, Navision, Sage oder sonst was.

Welcher Weg der für Dich geeignetere ist, hängt davon ab, ob Du ...

... die Software für den Verkauf an Kunden oder für diesen einen Fall entwickelst.

... genügend Zeit/Geld hast um eine abgekoppelte Anwendung zu entwickeln (Das ist zunächst erstmal einiges an Mehraufwand und außerdem kann man nicht mehr einfach bestehende Infrastruktur des WAWI-Systems nutzen, da sich das WAWI-System ja ändern kann).

... zusätzlichen administrativen Aufwand (2 Datenbanken, 2 Systeme = 2 x Admin-Aufwand) in Kauf nehmen kannst.

Hoffentlich helfen Dir diese Überlegungen, die für Dich richtige Entscheidung zu treffen.

Für neue Projekte möchte ich Dir empfehlen, nicht mehr in "Programmen" zu denken. Zukünftige Software wird nicht mehr aus großen Black-Box Programmen bestehen. Man geht dazu über viele einzelnen kleine Dienste/Komponenten zu bauen und diese zu "orchestrieren" (zusammenspielen lassen). Der Vorteil von so einer diensteorientierten (SOA) Architektur ist, dass die einzelnen Dienste autonom sind und nur über definierte Schnittstellen, die sie nach außen anbieten, angesprochen werden können. Man weiß was rein muss und was raus kommt. Da man Dienste meistens zentral auf Server verfügbar macht, können sie natürlich auch von unterschiedlichsten Clients (WinForms, Web, Office-Anwendungen, Mobile, Konsole) konsumiert werden. Außerdem können zentrale Dienste sehr gut in flexible Workflows (Siehe Windows Workflow Foundation, BizTalk Server) eingebunden werden.

Für einen Blick über den VFP-Tellerrand kannst Du Dir mal folgende Sachen ansehen:

http://msdn.microsoft.com/winfx/technologies/workflow/default.aspx
http://www.microsoft.com/germany/biztalk/einsatz/soa.mspx
http://www.microsoft.com/germany/biztalk/einsatz/eai.mspx
http://www.microsoft.com/germany/biztalk/einsatz/businessprocesses.mspx

128 Beiträge seit 2004
vor 18 Jahren

Hallo Rainbird,

Original von Rainbird
(Es muss kauzig sein, sonst würde es nicht mit FoxPro arbeiten und würde nicht historisch bedingt ohne Primärschlüssel laufen müssen)

Dass das System ohne jegliche PrimaryKeys (und dem Index darauf) arbeitet, erscheint mir persönlich auch sehr kauzig. Aber das geht mit den Entwicklern heim, und nicht mit dem Produkt. Effektiv kann man jedes DBMS durch Unkenntnis ruinieren. Insofern geht der "blame" an die Entwickler des WAWI und nicht an VFP.

Wie gesagt, ein sehr suspektes System.

Just my 2 cents...

Bis denne, JoKi

Enjoy AFP FAQ - Participate AfpWiki - Get Blogged by JoKi - Talk to me at VFP User Group Meeting