Anpassbare Sortierung in Container

Hallo

Ich habe eine Untertabelle mit mehreren Datensätzen zum Hauptdatensatz. Diese Datensätze müssen nummeriert werden und in dieser Reihenfolge später verkettet werden. Die Sortierung dient also nicht nur der Anischt sondern ist Bestandteil des Datensatzes.

Jetzt hab ich dafür einen SMALLINT angelegt, es geht i.d.R. um eine einstellige Anzahl pro Hauptdatensatz. Der Anwender soll jetzt aber die Reihenfolge ändern können, ähnlich wie z.B. bei Firewall-Regeln nach oben oder unten schieben können, also eins hoch bzw. 1 runter als Funktion würde schon genügen.

Ideal wäre ein Weg über den Container, nicht über den einzelnen Datensatz.

Per SQL würde ich sowieso sicherstellen, das immer eine Nummer gesetzt ist und es keine Nummer mehrfach gibt. Ich könnte das Feld für den Benutzer ausfüllbar machen und aufgrund der Änderung die Sortierung anpassen. Gibt es hier durch das CRM auch eine Funktion?

In der Large Solution haben wir 2 Stellen, die sich um etwas ähnliches kümmern.

Die eine Stelle ist der Gesprächsleitfaden, wo sich die Reihenfolge der Fragen entsprechend natürlich durch eine fortlaufende Nummer ergibt und die Reihenfolge aber durch die Anwender:innen leicht verändert werden können sollte:

Bei den Schaltflächen kommt ein Script und zur Absicherung (auch bei etwaiger direkter Bearbeitung einer Frage-Nummer) noch ein Trigger zum Einsatz.
image

Ist nicht ganz trivial :sweat_smile:, aber vielleicht können Sie das adaptieren.

Die andere Stelle ist ein Trigger zur Durchnummerierung der Belegpositionen, wenn die Positionsnummer direkt bearbeitet wurde.

Auch hier steckt der Teufel im Detail, z.B. wenn eine Position „ganz nach hinten“ oder „ganz nach vorne vor den ersten“ durch direktes Bearbeiten der Nummer verschoben werden soll. Da haben wir auch in den letzten Wochen diesbezüglich etwas aktualisiert, ich hänge den aktuellen SQL-Code mal noch hier rein… („as is“ :wink: )

Large5_RecalcSalesDocument.sql.txt (38,7 KB)

Spoiler: ist noch weniger trivial als die erste Stelle. :roll_eyes: (sorry :see_no_evil:)

Gutes Gelingen!

zur Lösung 2:
Also ich hab das mal überflogen (der SQL Code macht ja eine ganze Menge abseits der Reihenfolge). Ich sehe das richtig das hier der Anwender einen Datensatz bearbeitet (in diesem Fall direkt im Contrainer der alle Datensätze zeigt und nicht in der Ansicht dahinter) und selbstständig eine beliebige Ziffer eintippt. Der AFTER-Trigger [dbo].[cmbt_Trigger_Solution_SalesDocumentItems] (glaube ich?) ruft dann Prozedur [dbo].[cmbt_sp_RecalcSalesDocumentItemPosNo] auf. (Letzteres habe ich noch nicht genau gefunden aber ich denke so isses gedacht :slight_smile: ), Die Prozedur sortiert dann neu.

Mir ging es um die Grundidee der Eingabe über ein Feld im Datensatz, so hätte ich es auch gemacht und dann mit SQL im Backend alle anderen Datensätze umlegen.

zur Lösung 1:
Was mich mehr reizt sind die Script Buttons (ich habe derzeit noch keine aktuelle Large Solution, kann daher nicht nachgucken), die aktualisieren vermutlich den markierten Datensatz im Container und erhöhen oder verringern den Wert um 1? Oder machen die die ganze Sortierung aller Datensätze?

Ich denke ich mache die Sortierung auf jeden Fall in SQL. Da meine Trigger sehr wenig mit Variablen arbeiten und ich eigentlich immer mehrere Datensätze gleichzeitig abfahre würde ich das sowieso erstmal selbst versuchen. Aber ein Script-Button welcher eine Spalte auf dem markierten Datensatz um 1 erhöht oder reduziert, genau das fehlt mir. Das Backend SQL versuche ich erstmal selbst zu schreiben (stecke nur grade noch an einer anderen Stelle fest…).

die aktualisieren vermutlich den markierten Datensatz im Container und erhöhen oder verringern den Wert um 1? Oder machen die die ganze Sortierung aller Datensätze?

Ich glaube man kann das nicht richtig voneinander trennen, denn wenn ich mir das überlege (hab nicht nachgesehen), muss die Nummer des aktuellen Datensatzes ja mit der seines Nachfolgers (bei Pfeil nach unten) bzw Vorgängers (bei Pfeil nach oben) GETAUSCHT werden. Dh es müssen auf jeden Fall zwei Datensätze angefasst werden und VORHER die ID des Nachfolgers/Vorgängers ermittelt werden.

Ein Renummerieren-Trigger käme dann beim DELETE (die Lücke schließen) und bei einem INSERT (leere Nummer verhindern) und bei UPDATE (bei direktem Editieren einer Nummer Duplikate korrigieren - AUSSER* - und das ist der Knackpunkt - es handelt sich gerade um den vorgenannten scriptseitigigen Tauschvorgang in mehreren Schritten) zum Einsatz.

*) Idee: zuerst wird beim Nachfolger/Vorgänger und beim aktuellen Datensatz eine „Spezialnummer“ eingetragen, über die der Trigger dann jeweils „gesagt bekommt“, dass er mit dem Datensatz explizit nichts tun soll, und dann erst erfolgt der Tausch der eigentlichen Nummer. Also eigtl. 4 Schritte. Nette Denksportaufgabe. :nerd_face:

Let’s SQL

demo tabelle.txt (784 Bytes)
Die Tabelle speichert Namen, die wesentlichen Typen sind Anrede, Vorname, Nachname.

demo trigger.txt (2,6 KB)
Der Trigger sortiert über alle Einträge zu einem fk_per, Anrede kommt aber immer zuerst, dann Vorname(n) und Nachname(n). Kommen neue Datensätze rein (eine Prozedur läd da Namen in großer Zahl rein) wird immer hinten angehängt. Wird ein Eintrag aktualisiert nimmt er immer den Platz mit seiner neuen Nummer ein, die anderen wandern.

Beispiele:
Anrede 1
Vorname 2
Vorname 3
Nachname
Gehe ich bei Vorname 3 rein und setze ihn auf 2 tauscht er den Platz. Setze ich ihn auf 1 ebenfalls denn er kommt immer nach der Anrede.

Ich muss noch Feinschliff machen und ich hoffe es ist performant wenn viele Daten in der Tabelle. Und ja, ich könnte einen Button brauchen der wirklich nur den markierten Datensatz +1 oder -1 setzt, das müsste eigentlch gehen.

Okay hier ein paar Fixes.

demo tabelle.txt (785 Bytes)

  • TINYINT läßt sich im CRM nicht für die Sortierung verwenden, daher SMALLINT
    demo trigger.txt (3,4 KB)
  • Hilfstabelle für die Reihenfolge der Typen habe ich ausgelagert, da ich sie an zwei Stellen zum sortieren brauche.
  • In t1 habe ich nur die Anzahl der Einträge pro Typ ermittelt (DISTINCT war hier überflüssig), ich brauche aber tatsächlich später die Anzahl aller Einträge für den Typ und alle Typen in der Reihenfolge davor. Ich habe das in „Offset“ umbenannt. Immer wenn ein Datensatz neu ist wird er in die bestehenden Datensätze eingefügt und zwar hinter den Typen mit einer kleineren Sortierung und hinter allen bestehenden Einträgen des selben Typs. Der Offset ist genau diese Stelle.
  • Bei der Sortierung habe ich Datensätze mit einer aktualisierten Position immer vor den Datensatz gestellt der die Position grade inne hatte (mit - 5). Dadurch konnte man nicht durch Position + 1 eine Zeile tiefer gehen, effektiv kam man wieder davor. Daher hier noch ein zusätzlicher Fall im CASE mit + 5 für die Sortierung.

Ich habs getestet, alles auf einem Datensatz. Ich glaube alle Bugs sind raus.

Könnten Sie das Script der Buttons mit Pfeil nach oben/unten aus dem Screenshot „Gesprächsleitfaden“ bereit stellen? Ich hab keine Ahnung von Visual Basic oder was dahinter ist. Auch muss man ja Bezug nehmen auf den markierten Datensatz im Container, keine Ahnung wie ich das anstelle.

Könnten Sie das Script der Buttons mit Pfeil nach oben/unten aus dem Screenshot „Gesprächsleitfaden“ bereit stellen? Ich hab keine Ahnung von Visual Basic oder was dahinter ist. Auch muss man ja Bezug nehmen auf den markierten Datensatz im Container, keine Ahnung wie ich das anstelle.

Klar. :slightly_smiling_face:

MoveQuestionInContainer.vbs (21,3 KB)

So konnte mir das grade angucken, viel mehr als ich erwartet hätte. Verstehe ja leider nicht viel VB aber das Script durchläuft tatsächlich mehrere Datensätze und das macht es auch irgendwie mehrfach. Mit oCurrentContainerRecord = oContainer.CurrentRecord bekomme ich wohl den Datensatz den ich verändern will, da muss ich mal schauen ob ich genau den einen Datensatz auch geändert kriege.

Ansonsten geht es ja auch durch ändern der Spalte im Container schon relativ einfach. Wenn ein Datensatz geändert wird macht mein Trigger alles andere.

Ich hab mir das heute nochmal angeguckt aber was das vbs-Script angeht gebe ich auf :unamused:

Ich sehe zwar ganz grob was es alles macht aber das brauche ich größten Teils nicht. Nur das alles raus zu nehmen ohne Plan wird vermutlich nicht mehr laufen. Und bei dem was ich brauche habe ich Mühe die passenden Stellen überhaupt zu finden.

Mir scheint
oRecord.SetContentsByName CStr(„QuestionNo“), (Replace(nQuestionNoOld, „.“, „,“) - nMoveCount)
verändert dann tatsächlich den Datensatz oder? Kann ich hier auch abkürzen und sagen
oRecord.SetContentsByName CStr(„sortierung“), (sortierung -1)
?
„sortierung“ ist meine Ziel-Spalte. Wird sie geändert übernimmt der Trigger schon alle anderen Datensätze.