Seite 2 von 3

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:07
von Manfred
OK,

das war ja die Vermutung von Jan und mir, oder so. Also rückt da nix nach. Im Nachhinein betrachtet auch nachvollziehbar, weil der Unique ja beim Neuaufbau erstellt wird. Jetzt leuchtet es mir ein. Ist ja nicht wie bei einem Standardindex.

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:07
von Jan
Zum allgemeinen Verständnis, warum ich den Unique habe: Es gibt eine eigenständige kleine Applikation, die diese dbf browsed. Und da sollen aus reiner Übersichtlichkeit von jeder Nummer immer nur ein Datensatz angezeigt werden. Was für mich am Einfachsten über den Unique-Index geht. Der wird auch ausschließlich nur für diesen Zweck genutzt, aber ansonsten natürlich grundsätzlich mitgeführt, sollte also immer aktuell sien, egal, was in der dbf geschrieben oder gelöscht wurde.

Jan

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:09
von Jan
Manfred,

mir ist das garnicht klar. Denn auch der "normale" Index rückt ja städnig nach, wenn die entsprechenden Datensätze geändert, hinzugefügt, oder gelöscht werden. Warum sollte das nicht auch im Unique-Index passieren? Auch einen "normalen" Index muß ich nicht reindizieren, nur weil ich irgendwas gelöscht habe.

Jan

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:14
von Manfred
Hm,

ich habe zuwenig Erfahrung damit. Ich benutze Unique in nur einer Tabelle bei mir, habe aber dort noch nie so ein Problem des Ablaufes gehabt wie Du jetzt. Bisher hat sich nichts ergeben deshalb und darum hoffe ich, dass alles so klappt, wie ich es mir damals gedacht hatte. Löschen und Anhängen vollziehe ich in besagtem Index auch den ganzen Tag. Aber der Index wird nur recht selten komplett neu erstellt, sondern immer nur aktualisiert.

Wobei ich jetzt auch in der Anleitung nichts erkennen kann, das darauf hinweist, dass der Unique Schlüssel nur einmal und zwar bei einer Erstellung funktioniert. Und das hört sich ja für mich so an, wenn ich das hier lese.

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:18
von Manfred
Hier, das Problem hatten wir doch schonmal woanders.

meine Anfrage an Alaska und die Antwort.
wie in der Dokumentation bereits ausgeführt, es wird ein Schluessel
immer nur einmalig aufgenommen. Das heisst, ist der Schluessel bereits
im Index dann nicht, andernfalls ja. Es gilt also "der erste gewinnt".

Welches der erste ist, hängt wiederum davon ab in welcher Reihenfolge
die Werte in der Tabelle entstanden sind. Bei reinen Append-Sequencen
wäre der im index enthaltene Schluessel/Record identisch mit dem Ergebniss
einer Re-Index operation. Wurden aber die Schluesselwerte durch
REPLACE/UPDATE verändert und führt dies zu einem Schlüssel der als
N-ter gilt, also nicht dem Index hinzugefügt wird so kann ein Reindex
diesen Schluessel/Record sichtbar machen da es nun eventuell der erste ist.

Das UNQIUE oder auch CANDIDATE attribute wurde geschaffen um
Primärschluessel zu verwalten, diese sind immer unqiue und im Regelfall
über den Lebenszyklus eines Datensatzes stabil unterliegen also keinen
Änderungen.

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:20
von brandelh
Hallo Jan,

das hat nix mit magischer Blackbox zu tun. Der einfachste Index ist eine Liste mit Verweisen auf Datensätze, der Vorgänger und Nachfolger ergibt sich aus der Reihenfolge im Index.
Denke an ein Array und eine do while ! eof() Schleife ...
// kompletter Index !

Code: Alles auswählen

aMeineAdressen := {}
use Adressen 
do while ! eof()
   aadd(aMeineAdressen, { upper(field->Name), recno() }
   skip
enddo
aSort(aMeineAdressen....)
jede Änderung eines Datensatzes, muss im entsprechenden Array-Eintrag ebenso erledigt werden und auch das ASORT().
// kompletter Index !

Code: Alles auswählen

aMeineAdressen := {}
use Adressen 
do while ! eof()
   if MeineBedingung() // z.B. Prüfung ob es den Satz schon gibt ! 
      aadd(aMeineAdressen, { upper(field->Name), recno() }
   endif
   skip
enddo
aSort(aMeineAdressen....)
wer hier nicht ins Array aufgenommen wird, kann später nicht "irgendwie" erscheinen !
das ist jetzt eine grobe Erklärung, Xbase++ Indexe sind wohl als binäre Bäume (b-Tree) angelegt, aber das Grundprinzip bleibt.

Ein anderes Beispiel, nimm ein 1000 Seiten Buch und den Wortindex, ein Wort kommt an 5 Stellen vor, also kommen 5 Seitenverweise nach dem Wort im Index.
Wenn man nun nur das erste Auftreten erfassen würde, und genau diese Seite wird gelöscht. Woher sollte der Indexverwalter auf die Schnelle wissen, wo sonstwo noch so ein Wort steht ?
Bei der Erstellung hieß es nur den ersten Eintrag und somit wird nur mit diesem Datensatz (Buchseite) syncronisiert.

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:21
von georg
Hallo,


vielleicht ist der Denkansatz falsch, den Ihr hier übt ...

Wir erstellen eine Datei mit folgender Feldern, das erste Feld ist als UNIQUE Schlüsselfeld definiert:

#1 Müller; Jan;
#2 Maier; Claus;
#3 Müller; Fritz;
#4 Schulze; Erwine

Mit dem Schreiben des ersten Satzes sieht der Index (stark vereinfacht) so aus:
Müller #1

Nach dem zweiten Satz:
Maier #2
Müller #1

Nach dem dritten Satz:
Maier #2
Müller #1

Nach dem vierten Satz:
Maier #2
Müller #1
Schulze #4

UNIQUE bedeutet, dass nur solche Sätze in den Index aufgenommen werden, deren Suchbegriff noch NICHT vorhanden ist. Da es einen Satz mit "Müller" als Schlüssel gibt, landet der dritte Satz auch nicht im Index.

Folglich kann auch kein Satz "aufrücken", da er aufgrund der Index-Definition nicht "sichtbar", bzw. im Index vorhanden ist. Und die DBE sucht auch nicht nach eventuellen Kandidaten für eine Aufnahme in den Index (würde die DBE das tun, würden wir uns bei grossen Dateien sehr beschweren!).

Eine denkbare Alternative wäre, einen zweiten (n-ten) Index zu haben, der die gleiche Definition, aber nicht UNIQUE hat. In diesem Index nach dem Schlüssel suchen, wenn ein Satz gefunden wird, den Schlüssel dieses Satzes ändern und zurück auf den ursprünglichen Wert ändern:

#3 Müller; Fritz => Müller*; Fritz
#3 Müller*; Fritz => Müller; Fritz

Dadurch sollte bei der Prüfung durch die DBE festgestellt werden, dass es noch keinen Eintrag für Müller gibt, und dann wird dieser aufgenommen.

Es ist aber auch möglich, dass ein Datei->(FieldPut(KeyField)) den gleichen Effekt hat, das habe ich jetzt nicht ausprobiert.

Verständlich ausgedrückt?

Bei "richtigen" DBMS Systemen bedeutet UNIQUE übrigens "lass es knallen, wenn einer versucht, einen zweiten Satz mit einem vorhandenen Schlüssel zu schreiben". Und das "Knallen" sind nicht Sektkorken, sondern Laufzeitfehler.


Gruss,

Georg

P.S.: Hubert, danke, genau meine Überlegung.

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:32
von brandelh
Jan,

die Verwaltung jedes Indexes kostet Zeit und bei großen Dateien und eventuell langen Indexausdrücken auch schon ganz ordentlich.
Je weniger Indexe um so besser !

Du solltest also nur den normalen benutzen und dann wenn du daraus nur einen Teil benötigst, den Rest mit einer Funktion ausfiltern !
Das meine ich jetzt wörtlich, z.B. etwa so ...

Code: Alles auswählen

MyUnique( )
set filter to MyUnique( bIndexSchluessel ) // z.B. {|| upper(name) }
go top 
...

function MyUnique( bIndexSchluessel )
     local aIndex := {}
     if bIndexSchluessel = NIL // zurücksetzen
       aIndex := {}
       return NIL
     else
       if 0=aScan(aIndex,bIndexSchluessel) 
         aadd( aIndex, eval(bIndexSchluessel) ) // neuer Satz, merken und anzeigen.
         return .t.
       endif
     endif
return .f.

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:34
von Jan
Ich habe das jetzt als Workaround erstmal so gelöst: Nach der ganze Löscherei gehe ich in dem Scope auf den ersten Satz, lese den Wert aus, locke den Satz, schreibe den Wert zurück, unlocke. Dadurch, das ich den Wert (ansosnten vollkommen unnötig) schreibe, wird der Unique-Index aktualisiert.

Ichnhalte das Verhalten des Unique-Indexes aber trotzdem für falsch, so wie das jetzt ist. Wie oben schon angemerkt, wird ja auch sonst jeder Index zur Laufzeit jederzeit aktualisiert. Das sollte in meinen Augen auch heir so sien.

Jan

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:41
von georg
Hallo, Jan -


scheinbar willst Du nicht verstehen, wie ein UNIQUE Index arbeitet.

DAS IST KEIN NORMALER INDEX. PUNKT.

Ein UNIQUE Index ist quasi (!) eine Art Filter und nimmt nur solche Sätze in den Index auf, deren Schlüssel noch nicht vorhanden ist. Du kannst also eine Datei mit 20,000 Sätzen haben, der UNIQUE Index kennt aber nur 500 davon. Wird ein Satz gelöscht, hat die Datei 19,999 Sätze, und der UNIQE Index kennt nur noch 499 davon.

Egal, was Du meinst, hier hat die Dokumentation die Nase vorne:
UNIQUE

The option UNIQUE specifies whether several records having the same index key are each stored in the index file. When the option is specified, a unique index is created where each key value appears only once. If the option is missing, the current setting of Set(_SET_UNIQUE) is used.
Dadurch, dass Du "unnötig" den Datensatz "aktualisierst", wird die DBE angestossen, prüft, findet den Schlüssel nicht im Index und nimmt ihn JETZT auf.

Eventuell probierst Du ja mal CANDIDATE aus, eventuell trifft das Dein Problem besser. (Leider ist hier die Dokumentation noch dürftig.)


Gruss,

Georg

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:42
von Markus Walter
Hi Jan,

ich glaube, Du hast Tom nicht verstanden. Aber nochmal, Hubert hat Recht. Ein Unique-Index hat von jedem "gleichen" Datensatz nur den ersten gespeichert (nicht alle und "verdeckt" nur die doppelten). Wenn Du nun diesen Datensatz löschts, siehts Du die Doppelten noch immer nicht. Dazu müsstest Du in der Tat neu indexieren.

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 13:57
von brandelh
Hallo Jan,

dein WORKAROUND - erneutes Speichern des ersten der passenden Datensätze - funktioniert und dürfte für dich die schnellste Lösung sein.

DU kannst das so tun, weil DU weißt welcher Index den richtigen ersten Datensatz kennt und diesen dann neu schreibst,
was zur Neuaufnahme dieses Satzes führt. Du bist ein intelligenter Mensch und hast die komplette Datenabhängigkeit im Kopf.

Die DBE kann das so nicht wissen, sie "könnte raten" welcher andere offende Index vielleicht passen könnte und dann versuchen
den richtigen ersten Satz zu finden ... aber wenn da was schief ginge wäre das Gemotze groß.
Allzu schnell wäre die automatik bestimmt auch nicht und somit wären andere wieder unzufrieden.

Im Zweifel hat es dbase auch schon so gemacht (sicherlich !) und dann kann Xbase++ ja gar nicht anders :wink:

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 14:16
von Tom
Im Prinzip müsste die DBE das wissen. Wenn ich einen UNIQUE-Index nicht als Ersatz eines Primary Keys benutze, sondern beispielsweise, um in einer Such-Schnellleiste alle verwendeten Anfangsbuchstaben des Nachnamens anzuzeigen, kann ich den UI hierfür verwenden. UNIQUE heißt ja tatsächlich zunächst nur, dass jeder "Treffer" (IndexKey()) nur einmal aufgenommen wird. Bei einem echten Primärschlüssel dürfte es übrigens auch nur einen Datensatz hierzu geben! Das ist also nicht dasselbe. Ergänze ich, prüft die DBE ja auch, ob der Key schon vorhanden ist - und agiert entsprechend, ergänzt also fallweise den Index oder eben nicht. In meinen Augen ist es ein Fehler, dass der Datensatz nicht nachrückt, wenn ein anderer gelöscht wird. Offenbar geschieht das ja auch, wenn ein REPLACE auf den Indexkey erfolgt.

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 14:18
von Martin Altmann
Tom hat geschrieben:Offenbar geschieht das ja auch, wenn ein REPLACE auf den Indexkey erfolgt.
Falsch! Da rutscht nichts nach, sondern der Eintrag wird dann neu aufgenommen.

Viele Grüße,
Martin

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 14:19
von Tom
@Martin: Semantische Feinheiten. :wink:

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 14:25
von brandelh
@ TOM

von der Technik aber ein riesen Unterschied, ob das System - wie von euch gefordert - bei jeder Änderung alle anderen Sätze prüfen soll,
ob diese jetzt auch relevant wären (sie sind nicht im Index, also sequenziell ! ),
oder ob wie im zweiten Fall ein Satz geändert wird und das System prüft, ob dieser Satz richtig im Index enthalten ist.

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 14:32
von Tom
Hallo, Hubert.

Klar, ich sehe dieses Problem auch - bei 100.000.000 Datensätzen kann das schon ein paar Millisekunden dauern, herauszufinden, ob ein Datensatz nachrückt. :wink: Dennoch müsste das entweder dokumentiert sein - denn es handelt sich nicht um einen echten Primary Key - oder sich eben anders verhalten.

Ich bin nämlich soeben darauf gestoßen, dass dieses Verhalten auch die Ursache für ein Problem sein dürfte, dem ich zuweilen begegne. Ich führe eine Sammeldatei, die Replikate der Einträge aus vier anderen Tabellen enthält, wobei es in diesen vier Tabellen Paralleleinträge geben kann. Damit diese nur einmalig in einem Kontroll-/Übersichtssystem angezeigt werden, hat die Sammeldatei einen UNIQUE-Index auf einen Schlüssel, den es in jeder "angehängten" Tabelle nur einmal geben kann, den es aber in einer Paralleltabelle auch (einmal) geben kann - in diesem Fall gehören die Datensätze zusammen. Aus dieser Sicht heraus sind auch Löschungen möglich - in den Einzeltabellen, wodurch dann auch ein Eintrag aus der Sammeldatei gelöscht wird. Wenn das genau jener mit dem Eintrag im UI ist, ist kein Eintrag mehr sichtbar.

Glücklicherweise enthält diese Sammeldatei nur ein paar hundert Datensätze maximal, ich kann also re-indexieren. Aber es ist interessant, auf diesem Weg zu erfahren, dass ich das auch tun muss. :wink:

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 14:50
von brandelh
Die Doku kann immer besser sein ...

besser wäre es wenn wir z.B. eine iVar SetSkipUnique hätten, welche bei .t. intern prüfen würde, ob der aktuelle Datensatz schon angezeigt wird.
Wenn ja, dann überspringen ... und das zur Laufzeit mit sofortiger Wirksamkeit.
Das würde zusätzliche Indexe sparen und wäre dennoch sehr schnell.

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 16:24
von AUGE_OHR
Jan hat geschrieben:Ich habe das jetzt als Workaround erstmal so gelöst: Nach der ganze Löscherei gehe ich in dem Scope auf den ersten Satz, lese den Wert aus, locke den Satz, schreibe den Wert zurück, unlocke. Dadurch, das ich den Wert (ansosnten vollkommen unnötig) schreibe, wird der Unique-Index aktualisiert.
GUT ... aber warum auf den 1st Datensatz ( im SCOPE ) und nicht den 2nd ?

du willst doch "danach" den 2nd Eintrag als "1st" in dem UNIQUE Index haben, oder ?

Code: Alles auswählen

GOTO (n2ndRecno)
REPLACE FIELD->NAMExxx WITH FIELD->NAMExxx
SKIP(0)

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 16:27
von brandelh
Wenn ich mich recht erinnere löscht er den Satz indem er zunächst das Indexfeld leert ... danach steht ein anderer Satz am Anfang des Scope und diesen ändert er.

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 16:30
von Jan
AUGE_OHR hat geschrieben:du willst doch "danach" den 2nd Eintrag als "1st" in dem UNIQUE Index haben, oder ?
Nein, warum sollte ich?

Jan

Re: Vorgehensweise bei Unique Index

Verfasst: Mo, 02. Apr 2012 16:41
von AUGE_OHR
Jan hat geschrieben:
AUGE_OHR hat geschrieben:du willst doch "danach" den 2nd Eintrag als "1st" in dem UNIQUE Index haben, oder ?
Nein, warum sollte ich?
ok ... dann meine ich wohl einen anderen SCOPE als du.
ich meine den SCOPE auf die selben Records wie UNIQUE aber eben "ohne" UNIQUE worauf ich "umschalten" bevor ich den 1st UNIQUE Eintrag lösche.

Re: Vorgehensweise bei Unique Index

Verfasst: Di, 03. Apr 2012 15:58
von Tom
Ich habe das bei mir gerade geprüft und festgestellt, dass ich tatsächlich in das gleiche Problem "gelaufen" bin. Ein Index (von 8 ) ist Unique und soll lediglich dabei helfen, aus ähnlichen Datensätzen nur den jeweils ersten Treffer anzubieten. Der Mechanismus hat aber weitreichende Konsequenzen - wenn dieser Index keinen Treffer liefert, wird davon ausgegangen, dass es natürlich auch keine "ähnlichen" Datensätze gibt. Dies ist wohl auch die Ursache für etwas irritierende Fehler, die wir hin und wieder festgestellt haben, wenn ein Datensatz gelöscht worden war, der quasi die Ähnlichkeitsreferenz darstellte, also im Unique-Index referenziert wurde.

Ich prüfe jetzt beim Öffnen/Einlesen der Tabelle, ob es für jeden Datensatz diesen "Ähnlichkeitseintrag" gibt - immerhin sind ja die 7 anderen Indexdateien jederzeit korrekt. Ist dies nicht der Fall, ersetze ich lediglich die indexrelevanten Felder mit den Inhalten, die sie sowieso haben, und erzwinge dadurch die Aktualisierung der Indexdatei. Funktioniert.

Dies vorab.

Ich muss allerdings den Darstellungen, bei einem Unique-Index würde es sich im Prinzip um einen ein-eindeutigen Primärschlüssel handeln, energisch widersprechen. Dafür bräuchte man keinen Unique-Index, denn ein ein-eindeutiger Primärschlüssel darf ohnehin nur einmal vorhanden sein - sonst ist es schlicht keiner. Es spielt also keine Rolle, ob ich hierfür einen normalen Index oder einen Unique-Index verwende.

Ein Beispiel für eine sinnvolle Anwendung eines Unique-Indexes wäre beispielsweise eine Tabelle mit Straßennamen, die man führt, etwa für das Auto-Vervollständigen in Adressdatensätzen (genau dafür verwende ich derlei auch). Diese enthielte, damit überhaupt eine sinnvolle Zuordnung möglich ist, natürlich auch noch Postleitzahl und Ortsnamen. Wird diese Tabelle jetzt nicht nur für das Matching verwendet, sondern etwa für eine Suche dargestellt, kann sie auch zunächst für die Suche nach Postleitzahl/Ortsnamen verwendet werden, um dann etwa in einem zweiten Schritt die zu diesem Ort vorhandenen Straßen anzubieten. In der Suche nach Ortsnamen will ich aber nicht fünfunddreißigtausend Einträge zu "Berlin" sehen, sondern nur einen - die 35.000 Straßen benötige ich erst in einem zweiten Schritt. Deshalb erzeuge ich u.a. einen Unique-Index auf Postleitzahl und/oder Ortsnamen, wodurch ich, wenn dieser Index führt, jeden Ort nur einmal angezeigt bekomme. Abseits dieser Suche wäre dieser Index natürlich auch für das Auto-Vervollständigen mit "Ausklappauswahl" in PLZ- und Ortsfeldern wichtig. Dies ist in meine Augen eine sinnvolle und richtige Verwendung von Unique-Indexen.
Lösche ich jetzt allerdings aus dieser Tabelle zufällig genau jenen Datensatz, der im Unique-Index referenziert wird (der erste, den ich für diese Kombination aus PLZ und Ort erfasst habe), wird mir dieser Ort überhaupt nicht mehr angeboten, wenn ich nach Ort/PLZ suche. Erst nach einer Re-Indexierung taucht er wieder auf.

Das ist in meinen Augen ein schwerer Fehler. Ich verstehe, dass es eine komplexe Aufgabe ist, in dieser Situation andere Datensätze nachrücken zu lassen, aber das ändert nichts daran, dass sich die Engine hier schlicht falsch verhält.

Re: Vorgehensweise bei Unique Index

Verfasst: Di, 03. Apr 2012 16:06
von Manfred
Das Thema sollten wir mal für die Devcon, oder spätestens für den nächsten Chat bereithalten.

@Tom, Danke für den Tipp. Auf sowas bin ich noch gar nicht gekommen. Jetzt erschließt sich mir auch, wozu man den Unique generell verwenden kann/könnte.

Re: Vorgehensweise bei Unique Index

Verfasst: Di, 03. Apr 2012 16:33
von Tom
@Manfred: Ja, dafür ist ein Unique-Index m.E. gedacht. Allerdings mache ich das nicht so wie in meinem Beispiel; ich führe unterschiedliche Tabellen für Straßen und Orte. :wink:
Und, ja, gute Idee, das im Chat oder auf der DevCon anzusprechen.

Übrigens gibt es ja seit 1.9 den "Candidate"-Index, der explizit nur für Felder/Ausdrücke erzeugt werden darf, die referentiell eindeutig sind. Ein Unique-Index ist demgegenüber ein schlichter "keine Wiederholungen"-Index. Auf einem eindeutigen Primärschlüssel enthielten Candidate/Unique- und Normalindex die gleichen Referenzen.