TDBGRID, neuen Eintrag auf Existenz prüfen

Für allgemeine Fragen zur Programmierung, welche nicht! direkt mit Lazarus zu tun haben.
Antworten
Lorca
Beiträge: 196
Registriert: Di 3. Nov 2020, 12:25

TDBGRID, neuen Eintrag auf Existenz prüfen

Beitrag von Lorca »

Hallo zusammen,

mir geht es zunächst um das grundsätzliche Verstehen.

Wenn ich eine TDBGRID und eine TDBEdit Komponente auf meiner Form ablege und diese dann mit einer aktivierten DataSource verbinde
sind die Daten (selbst im Design Modus) sofort vorhanden. Ich frage mich wie diese Daten gelesen ( oder Vllt. gepuffert ) werden.
Will ich diese Daten in einer StringGrid darstellen muss immer mit Lesebefehlen wie WHILE NOT EoF und Next gearbeitet werden.
Wie macht Lazarus das um diese Tabellen sofort nach der Aktivierung zu füllen, OHNE einen erkennbaren Lesebefehlt?

Hintergrund:
Wird ein neuer Satz in der Tabelle angelegt, möchte ich gerne eine Case Sensitive Existenz Prüfung durchführen, um zu vermeiden das ein doppelter Satz angelegt wird.
Hierzu würde ich gern einer TSTringList als Puffer füllen und diese dann prüfen. Dies geht jedoch nur wenn ich die komplette Tabelle einmal durch lese. Diesen Zeitaufwand möchte ich jedoch vermeiden und die Daten aus dem bereits gefüllten TDBGrid abgreifen bzw. die Methode welche die TDBGrid füllt, zu erweitern, um diesen Puffer zu füllen. Oder gibt es vllt. bereits einen vorhandenen Puffer, den ich abgreifen kann?

Das Problem dabei ist, ein Datensatz der im Status "Insert" ist, nicht mittels "Locate" auf Existenz geprüft werden kann da sich dann sofort der Status auf Browse ändert.


Hat jemand eine zündende Idee?

Viele Grüße
Lorca

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: TDBGRID, neuen Eintrag auf Existenz prüfen

Beitrag von Socke »

Lorca hat geschrieben:
Mi 22. Sep 2021, 08:03
Wenn ich eine TDBGRID und eine TDBEdit Komponente auf meiner Form ablege und diese dann mit einer aktivierten DataSource verbinde
sind die Daten (selbst im Design Modus) sofort vorhanden. Ich frage mich wie diese Daten gelesen ( oder Vllt. gepuffert ) werden.
Den Lesebefehl gibst du der DataSource, indem du sie auf "Aktiv" schaltest.

Die DataSource bzw. dessen DataSet des DBGrids solltest du nur für dieses DBGrid verwenden. Nach meinem Wissen kannst du weder direkt auf den Puffer des DataSets zugreifen noch über dieses iterieren ohne den Status (Browse <> Insert) zu ändern.

Falls du mit einer SQL-Datenbank arbeitest, kannst du eine (weitere) Query auf deiner Form platzieren und über ein SELECT EXISTS(SELECT * FROM mytable WHERE field = 'MeinNeuerWert') die Datenbank direkt nach auf den Wert abfragen. Dabei sind wie immer folgende Punkte zu beachten:
  • Ob die Prüfung Case-Sensitive durchgeführt wird, hängt von der Collation der Datenbank, Tabelle oder Tabellenspalte ab. Ggf. kann sie im Statement spezifiziert werden oder über ein Datenbankview nochmals anders als auf der Tabelle eingestellt werden.
  • Je nach Transaction Isolation Level können auch uncommittete Datensätze aus anderen Verbindungen gefunden werden.
  • Greifen mehrere Clients gleichzeitig auf deine Datenbank zu, kann deine Prüfung negativ auffallen (Datensatz ist noch nicht vorhanden), beim Einfügen des Datensatzes ist aber ein Datensatz bereits vorhanden (wurde zwischen Prüfung und Einfügen durch einen anderen Client angelegt).
In anderen Fällen würde ich mir einen separaten Puffer anlegen und darin die Existenz des Datensatzes prüfen. Hier kannst du im Zweifelsfall auch über eine weiters DataSet mit der selben Verbindung/Datendatei und Abfrage arbeiten. Je nach Quelle musst du diese dann nach Änderungen neu einlesen.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: TDBGRID, neuen Eintrag auf Existenz prüfen

Beitrag von wp_xyz »

Ich denke, du hast das Prinzip der Datenbanken und datensensitiven Komponenten nicht ganz verstanden. DBGrid, DBEdit etc. sind nur "dumme" Komponenten, die die in der Datenbank vorhandenen Daten darstellen bzw. deren Eingabe vermitteln. Sie "wissen" nichts über die Logik dahinter, also ob Records eindeutig sein müssen, oder ob dabei Groß-/Kleinschreibung zu beachten ist, oder wie die Records sortiert werden sollen.

Wenn du beim Bearbeiten eines Records verhindern willst, dass ein Feld in einem anderen Record schon vorhanden ist, dann darf das nicht das DBGrid prüfen (denn das könnte ja gar nicht verwendet sein), sondern du musst diese Aufgabe an die Datenbank selbst übertragen. Dafür gibt es das Konzept des Index. Erzeuge in der Dataset-Komponente einen Index (bei einer nicht-SQL-Datenbank mit Hilfe von IndexDefs) und lege dort fest, dass der Index eindeutig (ixUnique wählen) sein soll, wobei Groß-/Kleinschreibung zu beachten ist (ixCaseInsensitive nicht wählen).

Mit dieser Einstellung unterbindet die Datenbank die Eingabe eines in diesem Feld schon in einem anderen Record vorhandenen Werts automatisch, du musst nicht programmieren.
----------
Die Daten sind nach Aktiv-Setzen des Dataset sofort im DBGrid/DBEdit etc vorhanden, weil - um es kurz zu sagen - extra so gemacht ist. Das Dataset liest die Datenbank (genaugenommen nur etwas mehr als die benötigten Records) und gibt sie ans DBGrid/DBEdit weiter. Beim StringGrid gibt es keinen "Datenlieferanten". Man hätte natürlich ein "FileName"-Property definieren können, nach dessen Setzen die Datei gelesen und im Grid angezeigt wird, aber wegen der Vielfalt an Daten-Dateien hat man darauf - gottseidank - verzichtet. Spezialisierte Grids, z.b. das TsWorksheetGrid aus dem fpSpreadsheet-Paket, können das trotzdem, sie sind extra auf diese Situation vorbereitet.

Lorca
Beiträge: 196
Registriert: Di 3. Nov 2020, 12:25

Re: TDBGRID, neuen Eintrag auf Existenz prüfen

Beitrag von Lorca »

Hallo zusammen,

ganz herzlichen Dank für eure Antworten. Wie immer sehr konstruktive Vorschläge. :)

Es war zwar nicht das was ich mir vorgestellt habe, aber dies liegt nicht an euch. :). Wenn ich euch jedoch richtig verstanden habe kann man den Puffer nach dem aktivieren nicht auslesen. Ich hatte schon an mir selbst gezweifelt weil ich es nicht gefunden habe wo das passiert. Nun bin ich immer mehr der Meinung das dies sehr wahrscheinlich eine external Methode ist.

Ich arbeite mit SQLite 3. Meine Tabellen haben alle einen Unique Index. Leider habe ich hier noch keine Möglichkeit gefunden, segmentierte Primär Schlüssel anzulegen. (Mehrere Felder bilden als Kombination einen Primär Schlüssel)
Jedoch ist der Primär Schlüssel immer ein Integer Feld. Das macht das Leben etwas einfacher. Und hier kann es dann passieren das z.B. der Name "Hugo" bereits bereits mit der ID 5 vorhanden ist. Da ich keinen segmentierten Schlüssel habe kann es nun passieren das ein weiterer "Hugo" mit der neuen ID 25 angelegt wird.


@wp_xyz: >>"Die Daten sind nach Aktiv-Setzen des Dataset sofort im DBGrid/DBEdit etc vorhanden, weil - um es kurz zu sagen - extra so gemacht ist. Das Dataset liest die Datenbank (genaugenommen nur etwas mehr als die benötigten Records) und gibt sie ans DBGrid/DBEdit weiter."<<
Ja genau darum ging es mir ja, wie komme ich an diese Daten ran. :)


@Socke: Vllt. sollte ich mir bei Gelegenheit mal die Aggregat Befehle genauer anschauen.
Denn dieses SELECT EXIST war mir nicht bekannt. Und dieser Befehl scheint mir unter den gegebenen Umständen die beste und performanteste Lösung zu sein. Deshalb ganz lieben Dank :)


Viele Grüße
Lorca

charlytango
Beiträge: 843
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: TDBGRID, neuen Eintrag auf Existenz prüfen

Beitrag von charlytango »

Hoffentlich wird das kein Blödsinn und ich blamiere mich da wieder mal ;)

Wenn du SQLite verwendest, wirst du vermutlich über eine TSQLite3Connection die Verbindung zur Datenbank herstellen.
Eine TSQLQuery behandelt das SQL Statement und die TDataSource ist die Verbindung zu den datenbanksensitiven Controls (TDBEdit, TDBGrid etc)

Soweit ich weiß werden die (gepufferten und verfügbaren) Daten werden in der TSQLQuery in einem TBufDataset vorgehalten und verwaltet.
Natürlich kann man auf diese Daten auch zugreifen. Ob man direkt auf das TBufDataset vollinhaltlich zugreifen kann hab ich jetzt nicht nachgesehen. Aber es gibt jedenfalls Zugriffsfunktionen wie TSQLQuery .Locate und TSQLQuery.Lookup (das wohl eher deinen Bedürfnissen entspechen könnte weil es den "Datensatzzeiger" also den aktuellen Datensatz nach der Suche mittels eines TBookmarks wieder herstellt)

Trotzdem stellt sich mir angesichts deiner Texte eine ähnliche Frage nach dem Datenbankverständnis wie schon wp_xyz gestellt hat.
Dein Text mit den collated indizes verstärkt diesen Eindruck noch und es scheint mir als ob du in tabellenorientierten "Datenbanken" wie dBase, foxpro etc denken würdest. Und selbst dort haben collated (also kombinierte Felder) als Index nur bedingt geholfen (hatte damals an die 3 Mio Records in Mehrbenutzertabellen). Die habe ich nur in Spezialfällen zusätzlich zu anderen Indizes angelegt.
Mit Primary Indizes würde ich das ohnedies nie tun, schon aus Performancegründen.
In tabellenbasierten Datenbanken musste man den Index den man verwenden wollte aussuchen und angeben.

In "echten" Datenbanken -- also solchen wo die Datenhaltung von der Bearbeitung völlig getrennt ist, ist das Auswählen eines Index nicht (oder nur bedingt durch Syntaxumstellungen) möglich. Das erledigt die Datenbank durch Analyse des Statements und der vorhandenen Indizes selber. Und die Zugriffsstrategie sollte eine andere sein.
Nur das aus der DB anfordern was unbedingt nötig ist. Also keine SELECT * und die WHERE Klausel immer so dass ein Minimum an nötigen Daten transportiert werden muss -- denn meist bestimmt die Menge der transportierten Daten auch die Performance.

Für die Entscheidung welche Indizes nötig und hilfreich sind kann man nur Performancetests gegen eine Testdatenbank machen die idealerweise auch mit Testdaten befüllt ist. Andernfalls ist das besseres Kaffeesudlesen.
Indizes auf Primary Key und ggfs Foreign Key haben sich jedenfalls bewehrt. Viele Datenbanken lassen Primärschlüssel ohne existierenden Index auch nicht zu.
Wenn du wissen willst, ob der Optimizer einer SQL-Datenbank Indizes benutzt und vor allem welche, dann gibt es für die meisten Datenbanken externe Programme (Query-Analyzer) die zeigen wie ein bestimmtes SQL-Statement abgearbeitet wird.
Das ist aber alles nichts was Lazarus, die DB-Zugriffskomponenten und das DBEdit betrifft! Denn die haben davon keine Ahnung. Sollen sie auch nicht.
Mehrere Spalten in der SQL-Tabelle können natürlich auch abgefragt werden.
SELECT <Feldliste, nie *> FROM mytable WHERE myid=<key> AND field = 'EinWert'

Dein Problem doppelte Einträge in einer Tabelle zu verhindern beginnt schon mit der Definition was denn doppelte Einträge sind. Denn ein "Hugo" könnte ja auch sinnvollerweise mehrmals in der DB vorkommen dürfen. Letztlich kann diese Entscheidung nur der Benutzer treffen (wenn es sich nicht gerade um wirkliche Unique Daten handelt -- aber dann würde ich auch einen Unique Index auf die entsprechende Spalte setzten und die DB kümmert sich drum)
https://www.sqlitetutorial.net/sqlite-index/

Und dann ist noch wichtig ob es eine ein- oder Mehrbenutzerlösung werden soll.
Bei Mehrbenutzer musst du dir Gedanken machen wie du gleichzeitigen Zugriff oder gleichzeitiges Insert in den Griff bekommst. Mittlerweile ist bei allen Datenbanken das Erstellen eindeutiger integer Primary Keys in der Mehrbenutzerumgebung sauber implementiert.

Hoffe das hilft etwas und war nicht zu konfus ;)

Antworten