Reihenfolge manuell anpassen

Die Reihenfolge für die Anzeige von Daten legt man meist über Kriterien wie aufsteigend oder absteigend nach dem Alphabet eines Namens, nach Zeitangaben wie Uhrzeit oder Datum oder auch nach Zahlenwerten wie etwa Preisen oder Kilometerständen fest. Das sind Geschäftsdaten, die nicht primär der Sortierung dienen. Für manche Sortierkriterien gibt es allerdings keine passenden Felder: Hier müssen Sie selbst eines hinzufügen und für die richtige Reihenfolge Sorge tragen.

Beispiele für ein solches Szenario gibt es genug. Nehmen wir ein typisches CMS (Content Management System) für eine Webanwendung, das einen Artikel aus mehreren Elementen wie normalen Absätzen, Bildern und vielleicht noch interaktiven Elementen zusammensetzt. Irgendwo muss man hier festlegen, in welcher Reihenfolge die Elemente auf der Webseite angezeigt werden sollen – unabhängig davon, in welcher Abfolge man diese angelegt hat. Oder schauen Sie sich das Beispiel aus dem Beitrag Verknüpfte Datenbanken updaten (Shortlink 662) an: Die dort beschriebene Lösung führt beim Update eines Datenbankbackends eine oder mehrere SQL-Operationen aus, die natürlich nicht in willkürlicher Reihenfolge erfolgen können.

Fazit: Auch hier muss die Reihenfolge in einem separaten Feld festgehalten werden. Und schließlich noch ein Beispiel: Wenn Sie Aufgaben mit einer Datenbank verwalten, vergeben Sie normalerweise eine Priorität. Da aber meist mehr Aufgaben als Prioritätsstufen vorhanden sind und man dennoch eine Abfolge für die Abarbeitung festlegen möchte, legt man auch hier ein zusätzliches Feld fest, das die Reihenfolge vorgibt.

Wo ist aber nun der gravierende Unterschied zwischen Texten, Datums- oder Preisangaben und einem Reihenfolge-Feld, wie es in den zuvor beschriebenen Beispielen vorkommt Ganz einfach: Die Reihenfolge-Felder sind einzig und allein dafür da, die Reihenfolge festzulegen. In der Darstellung der Daten sind sie gar nicht sichtbar – außer durch die Reihenfolge, in der die Daten letztlich dargestellt werden.

Reihenfolge-Feld

Das manuelle Festlegen der Reihenfolge macht aber nur dort Sinn, wo die Menge der Datensätze deren Übersichtlichkeit nicht allzusehr einschränkt. Eine Tabelle mit 2.000.000 Datensätzen mit einem manuell einstellbaren Reihenfolge-Feld auszustatten ist nicht sinnvoll – uns ist jedenfalls kein Grund bekannt, warum man so etwas tun sollte. Für alle anderen Fälle verwenden Sie zum Einstellen der Reihenfolge ein Feld mit einem numerischen Datentyp, dessen Wertebereich von der Anzahl der zu erwartenden Daten abhängt. Damit Sie aber nicht doch irgendwann dummerweise in Fehler laufen, weil das Reihenfolge-Feld zu klein ausgelegt ist, verwenden wir, wie auch beim Primärschlüsselfeld üblich, den Datentyp Long.

Die Tabelle, die uns in diesem Beitrag begleiten soll und die Daten zum Anpassen der Reihenfolge liefern wird, sieht wie in Bild 1 aus und enthält neben dem Primärschlüsselfeld nur ein Feld namens Aufgabe und das Feld ReihenfolgeID.

pic001.png

Bild 1: Beispieltabelle mit Reihenfolge-Feld

Reihenfolge ändern

Wenn Sie mal für interne Zwecke ein Reihenfolge-Feld benötigen und keine Lust haben, das ändern der Reihenfolge zu programmieren, können Sie das Reihenfolge-Feld natürlich auch genau wie die übrigen Felder anzeigen und die Reihenfolge von Hand eintragen – wenn Sie dann noch eine Sortierung für dieses Feld festlegen, reicht dies für rudimentäre Anforderungen schon aus.

Komfortabler ist es jedoch, wenn ein Formular Steuerelemente zum ändern der Reihenfolge vorsieht. Die wichtigsten Standardfunktionen sind folgende:

  • Datensatz mit dem vorherigen Datensatz vertauschen
  • Datensatz mit dem folgenden Datensatz vertauschen
  • Datensatz an erster Stelle platzieren
  • Datensatz an letzter Stelle platzieren

Je nachdem, welches Steuerelement man zur Anzeige der Daten verwendet, kann man auch noch komfortablere Methoden einsetzen:

  • Datensatz an Position x verschieben
  • Mehrere Datensätze an Position x verschieben

Steuerelemente zum ändern der Reihenfolge

Je nachdem, welche Steuerelemente Sie zur Anzeige der Daten verwenden, sind die oben aufgeführten Optionen zum ändern der Reihenfolge mehr oder weniger aufwendig.

In der Datenblattansicht, der Endlosansicht oder im Listenfeld macht das ändern der Reihenfolge durch Verschieben eines Listenelements wenig Sinn, da man den Drag-and-Drop-Vorgang nur schlecht optisch darstellen kann. Dies funktioniert hingegen sehr gut im ListView-Steuerelement, das von Haus aus Möglichkeiten für Drag and Drop mitbringt, dafür aber bezüglich der Datenbindung schlechter ausgestattet ist. Wir schauen uns im Folgenden aber alle Möglichkeiten an.

Reihenfolge-Algorithmen

Bevor wir loslegen, überlegen wir uns allerdings erstmal ein paar schicke Funktionen, die wir später dann nur unter die Haube der Benutzeroberfläche stecken müssen.

Die Voraussetzungen für die nachfolgend vorgestellten Funktionen sind eigentlich immer gleich: Es sollen die Daten einer Tabelle mit einem Primärschlüsselfeld nach einem Wert in einem Reihenfolge-Feld sortiert werden. Was sich unterscheiden kann, sind die Namen des Primärschlüsselfelds und des Reihenfolge-Felds. Damit unsere Funktionen ihren Dienst unabhängig von diesen Oberflächlichkeiten verrichten und Sie nicht für jede neue Datenbank, in der Sie diese Funktionen einsetzen möchten, im Code der Funktionen herumbasteln müssen, bauen wir uns einfach eine kleine Abfrage namens qryOrder, die als Datenherkunft die zu sortierende Tabelle enthält – und davon auch nur das Primärschlüssel- und das Sortierfeld.

Und wie verhindert diese Abfrage nun, dass wir die im Code verwendeten Feldnamen je nach Zielanwendung und -tabelle ändern müssen Wir verwenden ganz einfach Aliasnamen für die Felder, die unabhängig von den Feldnamen immer gleich sind. Im Detail sieht das wie in Bild 2 aus – welche Geschäftsdaten sich in den anderen Feldern der zugrunde liegenden Tabelle befinden, ist für unsere Belange völlig uninteressant. Eine kleine Einschränkung gibt es jedoch: Das Primärschlüsselfeld muss einen numerischen Datentyp aufweisen. Wenn Sie einen String-Primärschlüssel verwenden, müssen Sie gegebenenfalls eine alternative Variante der hier vorgestellten Routinen erstellen.

pic002.png

Bild 2: Mit dieser Abfrage machen Sie alle zu sortierenden Tabellen gleich.

Reihenfolge-Feld füllen

Nicht immer ist man sich von vornherein im Klaren darüber, dass man für die Datensätze einer Tabelle ein zusätzliches Reihenfolge-Feld benötigt. Dementsprechend legt man vielleicht auch erstmal ein paar Datensätze an und fügt erst dann das Hilfsfeld für die Sortierung hinzu. Da dieses zu diesem Zeitpunkt noch leer ist, können Sie es entweder von Hand füllen oder diese Aufgabe durch eine kleine Routine erledigen lassen. Die Prozedur FillOrderID durchläuft dazu eine Datensatzgruppe auf Basis der Abfrage qryOrder, wobei es diese nach dem Feld OrderID vorsortiert – damit können Sie diese Routine auch verwenden, um Lücken im Feld OrderID zu schließen. Um eine Zählervariable zu sparen, erhält das Feld OrderID eines jeden Datensatzes den Inhalt seiner Eigenschaft AbsolutePosition, der bei 0 beginnt und deshalb jeweils um eins erhöht wird.

Public Sub FillOrderID()
Dim db As DAO.Database
Dim rst As DAO.Recordset
Set db = CurrentDb
Set rst = db.OpenRecordset("SELECT * FROM µ
qryOrder ORDER BY OrderID", dbOpenDynaset)
Do While Not rst.EOF
 rst.Edit
    rst!OrderID = rst.AbsolutePosition + 1
    rst.Update
    rst.MoveNext
Loop
End Sub

Reihenfolgewert für neue Datensätze

Nachdem nun das Reihenfolge-Feld aller vorhandenen Datensätze gefüllt ist, müssen wir uns noch um neue Datensätze kümmern. Dafür legen wir am besten auch eine kleine Routine an, die Sie in der Ereignisprozedur, die beim Neuanlegen eines Datensatzes ausgelöst wird, aufrufen.

Als Parameter braucht diese Funktion nur den Primärschlüsselwert des neuen Datensatzes (s. Listing 1). Die Routine öffnet dann zunächst eine Datensatzgruppe, die nur den Datensatz mit dem höchsten Wert im Feld OrderID enthält. Hier gibt es zwei Möglichkeiten:

Listing 1: Füllen der Reihenfolge-ID eines neuen Datensatzes

Public Sub FillNewOrderID(lngPKID As Long)
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim lngOrderMax As Long
Set db = CurrentDb
Set rst = db.OpenRecordset("SELECT TOP 1 OrderID " _
& "FROM qryOrder WHERE OrderID IS NOT NULL " _
& "ORDER BY OrderID DESC")
If Not rst.EOF Then
 lngOrderMax = rst.Fields(0)
End If
lngOrderMax = lngOrderMax + 1
db.Execute "UPDATE qryORDER " _
& "SET OrderID = " & lngOrderMax _
& " WHERE PKID = " & lngPKID, dbFailOnError
Set db = Nothing
End Sub
  • Es sind bereits Datensätze vorhanden: dann wird innerhalb der folgenden If…Then-Bedingung der höchste Wert des Reihenfolge-Werts in die Variable lngOrderMax geschrieben.
  • Die Tabelle ist noch leer: In diesem Fall wird der Inhalt der If…Then-Bedingung nicht ausgeführt und lngOrderMax behält den Standardwert 0.

In beiden Fällen erhöht die Routine den Wert von lngOrderMax um eins und schreibt diesen Wert dann in das Feld OrderID des neuen Datensatzes.

Zwei Datensätze vertauschen

Vertauschen hört sich in Zusammenhang mit der Reihenfolge von Datensätzen erstmal gut an. Schließlich vertauscht man gern mal einen Datensatz mit den darüber oder darunterliegenden Nachbarn oder auch mit dem ersten oder letzten Datensatz … oder Nein! Es gibt wohl kaum einen Einsatzzweck, der das Vertauschen etwa des ersten und eines irgendwo in der Mitte liegenden Datenatzes erforderlich machen würde. Eigentlich möchten Sie ja einen oder mehrere Datensätze verschieben und nicht vertauschen.

Wenn Sie einen Datensatz an die Stelle eines unmittelbaren Nachbarn, also des vorherigen oder nachfolgenden Datensatzes verschieben, sieht das so aus, als ob die beiden Datensätze vertauscht werden. Tatsächlich ist hier aber vom ändern der Position eines Datensatzes, also vom Verschieben die Rede. Vergessen wir das Vertauschen also und sprechen fortan von Verschieben.

Verschieben eines oder mehrerer Datensätze

Und wenn wir schon von Verschieben sprechen, dann reden wir auch gleich vom Verschieben nicht nur eines, sondern auch mehrerer Datensätze. Immerhin möchte man ja vielleicht auch mal ein paar Datensätze gleichzeitig in der Reihenfolge nach oben oder unten bewegen. Die Routine InterchangeOrder aus Listing 2 erledigt dies alles anhand dreier Parameter, welche die folgenden Informationen abfragen sollen:

Listing 3: Prüfen des aktuellen Datensatzes und Aktivieren oder Deaktivieren der Schaltflächen zum Verschieben von Datensätzen

Private Sub Form_Current()
    Dim bolFirst As Boolean
    Dim bolLast As Boolean
    Dim bolNew As Boolean
    bolFirst = Me.CurrentRecord = 1
    bolLast = (Me.CurrentRecord = _
    Me.RecordsetClone.RecordCount)
    bolNew = Me.NewRecord
    Me.Parent.cmdOK.SetFocus
    Me.Parent.cmdUp.Enabled = Not (bolFirst Or bolNew)
    Me.Parent.cmdTop.Enabled = Not (bolFirst Or bolNew)
    Me.RecordsetClone.MoveLast
    Me.Parent.cmdDown.Enabled = Not (bolLast Or bolNew)
    Me.Parent.cmdBottom.Enabled = Not (bolLast Or bolNew)
    End Sub
  • lngTargetOrderID: Reihenfolge-ID des Datensatzes, vor oder hinter dem die zu verschiebenden Datensätze landen sollen. Dabei wird vor den angegebenen Datensatz verschoben, wenn dieser sich in der Reihenfolge vor den zu verschiebenden Datensätzen befindet, sonst hinter den angegebenen Datensatz.
  • lngMoveFirstOrderID: Reihenfolge-ID des zu verschiebenden Datensatzes mit der kleinsten Reihenfolge-ID
  • lngMoveLastOrderID: Reihenfolge-ID des zu verschiebenden Datensatzes mit der größten Reihenfolge-ID. Dieser Parameter muss nur angegeben werden, wenn mehr als ein Datensatz verschoben werden soll.

Wenn lngMoveLastOrderID nicht übergeben würde, also nur ein Datensatz verschoben werden soll, stellt die Routine diesen Wert zunächst auf den von lngMoveFirstOrderID ein – das ist aus technischen Gründen wichtig, damit man die beiden Fälle gleich behandeln kann ("Verschiebe Datensatz 1 bis 3 hinter 5" funktioniert dann genauso wie "Verschiebe Datensatz 1 hinter 5"). Die Variable intCountMove wird dann mit der Anzahl der zu verschiebenden Datensätze gefüllt.

Anschließend folgt die wichtigste Fallunterscheidung dieser Prozedur: Sollen Datensätze nach vorne oder hinten verschoben werden In der Routine wird diese Frage so beantwortet: Ist die ID des ersten zu verschiebenden Datensatzes größer als die des Zieldatensatzes, dann geht es nach vorne (siehe Bild 3), sonst nach hinten (siehe Bild 4).

NachOben.emf

Bild 3: Verschieben von Datensätzen nach vorne vor einen anderen Datensatz

Wenn der Zieldatensatz vor dem ersten zu verschiebenden Datensatz liegt, öffnet die Routine eine Datensatzgruppe, die alle Datensätze vom Zieldatensatz bis zum letzten zu verschiebenden Datensatz enthält (in Bild 3 wären das die Datensätze 2 bis 5). Anschließend durchläuft die Routine alle Datensätze dieser Datensatzgruppe und führt eine von zwei Aktionen an ihnen durch:

  • Wenn die Reihenfolge-ID des aktuellen Datensatzes kleiner ist als die des ersten zu verschiebenden Datensatzes, dann wird dieser um die Anzahl der vorne einzufügenden Datensätze nach hinten verschoben (in Bild 3 sind das die Datensätze 2 und 3).
  • Deutet die Reihenfolge-ID darauf hin, dass der Datensatz einer der zu verschiebenden Datensätze ist, wird dieser entsprechend der Anzahl der zwischen dem Zieldatensatz und dem ersten zu verschiebenden Datensatz befindlichen Elemente nach vorne verschoben.

Es kann auch sein, dass einer oder mehrere Datensätze nach hinten verschoben werden sollen, wie es in Bild 4 der Fall ist. Dies wird im zweiten Teil der äußeren If…Then-Bedingung berücksichtigt. Auch hier erzeugt die Routine eine Datensatzgruppe, die alle betroffenen Datensätze enthält – in Bild 4 sind dies die Datensätze 2 bis 4.

NachUnten.emf

Bild 4: Verschieben eines Datensatzes nach hinten hinter einen anderen Datensatz

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar