0

Master–Detail-Layout (1:N Relation)

Ein Dashbaord habe ich mittels einer Seite aufgebaut. 
Weiter habe ich auf den Dashboard ein Auswahlfeld mit Name Ablaufdatum, welches die Optionen
- 1 mit Name "Ungefiltert",
- die Option 2 mit Name "Abgelaufen"
- und die Option 3 mit Name "Gut" hat.

Auf dem Dashboard ist ein Widget mit einer Tabellenansicht. In dieser Tabelle möchte ich Daten aus den Tabellen:
- Lebensmittelgruppierung und
- Lebensmitteldetail sehen.
Die Tabelle Lebensmittelgruppierung hat eine 1:N Beziehung zur Tabelle Lebensmitteldetail. Vom selben Lebensmittel x kann ich also mehrere Einheiten mit unterschiedlichen Ablaufdaten haben.

In der Tabelle Lebensmittelgruppierung habe ich den Name des Lebensmittels "Artikel". In der Tabelle Lebensmitteldetail habe ich das Datumsfeld Ablaufdatum, die Anzahl des Lebensmittels x.

Wenn auf dem Dashboard das Auswahlfeld auf Ungefiltert ist, dann soll das Ansichtswidget die Tabelle mit allen Lebensmitteln zeigen, egal ob der heutige Tag die Ablaufdaten der Lebensmittel überschreitet oder nicht.
Wenn das Auswahlsfeld auf Abgelaufen ist, dann soll das Ansichtswidget nur Lebensmittel zeigen, deren Ablaufdatum überschritten ist.
Und wenn das Auswahlfeld auf Gut ist, dann soll das Ansichtswidget nur Lebensmittel zeigen, deren Ablaufdatum noch nicht überschritten ist.

Da ein Lebensmittel über die 1:N Beziehung mehrere Artikel mit unterschiedlichen Ablaufdatum haben kann, soll die oben beschriebene Logik greifen wenn von z. B. 5 Artikeln lediglich einer das Ablaufdatum überschritten hat.

Nach mehreren Stunden des probieren wende ich mich an euch im Forum, kann mir jemand helfen? Danke vorweg.

11 Antworten

null
    • mirko3
    • vor 4 Tagen
    • Gemeldet - anzeigen

    Hallo Hermann. Versuche Folgendes. Lege den Code in das Formelfeld der Ansicht

    let auswahl := Auswahl;
    switch auswahl do
    case 1:
        (select Lebensmitteldetails)
    case 2:
        (select Lebensmitteldetails where Ablaufdatum < today())
    case 3:
        (select Lebensmitteldetails where Ablaufdatum >= today())
    end
    

    Füge dann eine neue Spalte mit einem Funktionsfeld hinzu und schreibe dort den Code hinein:

    Lebensmittelgruppierung.Artikel
    

    oder ziehe das entsprechende Feld per drag-and-drop herüber. Dann sollte es so funktionieren.

    Im Grunde braucht dein Auswahlfeld nur zwei Felder: Abgelaufen und Gut. Wenn nichts angewählt wird, dann wird alles angezeigt. Nach click wird gefiltert. Dann sieht es so aus. Mirko

    let auswahl := Auswahl;
    switch auswahl do
    case 1:
        (select Lebensmitteldetails where Ablaufdatum < today())
    case 2:
        (select Lebensmitteldetails where Ablaufdatum >= today())
    default:
        (select Lebensmitteldetails)
    end
    
      • hermann_huerzeler
      • vor 2 Tagen
      • Gemeldet - anzeigen

       Hallo Mirko, ich wertschätze deinen Effort sehr, vielen Dank. Funktioniert wie gewünscht.

    • Customer Support Ninox
    • uwe_groegor
    • vor 3 Tagen
    • Gemeldet - anzeigen

    Wenn man das Script von Mirko noch etwas optimierter im Hinblick auf Datenmenge und Performance ausrichten möchte, würde ich es wie folgt abändern.
     

    let auswahl := Auswahl;
    let vTbl := select Lebensmitteldetails where Nr;
    switch auswahl do
    case 1:
        (vTbl)
    case 2:
        (vTbl[Ablaufdatum < today()])
    case 3:
        (vTbl[Ablaufdatum >= today()])
    end
    
    

    Weshalb beim ersten 'select' die Klausel 'where Nr'?
    Ein Select mit einer where Klausel wird in Ninox immer schneller bearbeitet als ein reines 'select'.
    Das fällt bei Datenbanken mit überschaubarer Anzahl an Datensätzen noch nicht so ins Gewicht.
    Im laufe der Zeit kommen aber immer mehr Datensätze dazu und da kann es dann doch schon Unterschiede machen wie lange ein Ergebnis auf einen select auf sich warten lässt, wenn bspw.mehrere 'select' in einem Script genutzt werden müssen.

    Ein zweiter Grund ist für die Zuordnung des select zu einer Variablen ist die einmalige Ausführung des 'select' im Script. Das fällt zwar in der 'case'-Prüfung nicht so ins Gewicht, aber wenn man es sich frühzeitig aneignet, vergisst man es auch bei späteren umfangreichen Skripten nicht und muss sich nicht bei performance Problemen in dieser Hinsicht wundern.

      • Fred
      • vor 3 Tagen
      • Gemeldet - anzeigen

       Just to be clear:

      select TableName where Nr (or Id)
      
      is faster than
      
      select TableName
      • Customer Support Ninox
      • uwe_groegor
      • vor 2 Tagen
      • Gemeldet - anzeigen

      Yes

      • hermann_huerzeler
      • vor 2 Tagen
      • Gemeldet - anzeigen

       Hallo Uwe, es ist wunderbar, wie hier einem geholfen wird. Auch dir, vielen Dank.

      • hermann_huerzeler
      • vor 2 Tagen
      • Gemeldet - anzeigen

       Hallo Fred, danke für deine Ergänzung, nehme ich mit auf den Weg.

      • Gotje_Ing
      • gestern
      • Gemeldet - anzeigen

       
      Moin Uwe,

      ich kann diese Behauptung nicht bestätigen.
      Als Test habe ich eine Datenbank aufgebaut, in der 500.000 Einträge mit jeweils einem Textfeld, gefüllt mit 10 zufälligen Worten liegen.

      Um lokales Caching im Browser auszuschließen, wie auch weitere Netzwerk-Einflüsse, habe ich die Selects jeweils einzeln in ein do as transaction gekapselt. Für jedes Select wird die Laufzeit innerhalb des do as transaction ausgerechnet und ein neuer Eintrag als Log erstellt. 
      Der Code sieht so aus:
       

      for i in range(0, 1000) do
          do as transaction
              let startTime := now();
              select Data;
              let elapsedTime := number(now() - startTime);
              let myTime := (create 'Select Timing Results');
              myTime.('Time in ms' := elapsedTime);
              myTime.(Auswahl := 3)
          end
      end
      

      Wichtig ist hier, dass die Zeitdifferenz nur den Select betrachtet, nicht das create. Auch die For-Schleife darf nicht im do as transaction laufen, da sonst weitere serverseitige Optimierungen greifen, die das Ergebnis verfälschen würden.

      Es wird verglichen 

      select Data;

      vs

      select Data where Nr;

      Die Ergebnisse sehen wie folgt aus:

      select Data:

      Durchschnitt: 598.51ms, Standardabweichung: 144,07ms

      select Data where Nr:

      Durchschnitt: 596.57ms, Standardabweichung: 183,29ms

      Varianz: 0,2642 und damit statistisch nicht signifikant

       

      Grafisch sehen die Laufzeiten in etwa so aus:

      X Achse: Gruppen von je 10ms 

      Y Achse: Anzahl der Durchläufe, die in die jeweilige Gruppe fallen.

      Bitte nicht falsch verstehen. 

      select X where Y ist die mächtigste Waffe um Performance zu optimieren.
      Ein einfaches select X where Nr ist aber nicht ausreichend.


      Die Vorteile von select where entstehen durch folgenden Punkte:
      - select ohne where lädt im Browser immer alle Datensätze komplett herunter. Dadurch entstehen Netzwerk-Verzögerungen, da mit höherer Anzahl an Records in der Regel auch deutlich mehr Daten übermittelt werden. Select where lädt dagegen nur die Daten vom Server, die nach dem Filter übrig bleiben. Bei select X where Nr bleiben nach wie vor alle Daten übrig, da jeder Eintrag dem Filter entspricht.
      - select where hat nach meinen Tests einige Optimierungen die auf dem Server ausgeführt werden, die im Client nicht zu Verfügung stehen. 
      - Optimierungen mit do as transaction sind in Verbindung mit einem guten select where Filter doppelt effizient, da der Arbeitsspeicher des Servers nur die Daten halten muss, die dem Filter entsprechen. Bei einem "do as transaction select X[A = Bedingung] end" müssen alle Datensätze im Arbeitsspeicher gehalten werden. Das ist nicht sinnvoll. Es hilft hier auch nicht, wenn die selects zuvor auf Variablen geschrieben werden ohne ein select where zu nutzen.

      Weitere Optimierungen kommen durch die Reihenfolge der Filterargumente. Diese sollten so aufgebaut werden:
      Boolean, Numbers, Date/Time/alle Anderen, Text
      Innerhalb eines Datentyps sollten die Filter so angeordnet werden, dass die vorderen Argumente die größte Anzahl Einträge herausfiltern. Also ein Boolean-Vergleich, der nur 3 von 1000 Einträgen herausfiltert sollte hinter einem Vergleich stehen, der 500 von 1000 Einträgen herausfiltert.

    • Ninox-Professional
    • planoxpro
    • gestern
    • Gemeldet - anzeigen
     said:
    Weitere Optimierungen kommen durch die Reihenfolge der Filterargumente. Diese sollten so aufgebaut werden: Boolean, Numbers, Date/Time/alle Anderen, Text

    Das habe ich mal anders gelernt. Man solle die Reihenfolge der Filterargumente nach inhaltlichen Kriterien bestimmen, also zuerst das, bei dem erwartbar am wenigsten Datensätze übrig bleiben, weil entsprechend weniger Datensäte auf das folgende Argument geprüft werden müssen. Also unabhängig vom Datentyp.

      • Gotje_Ing
      • gestern
      • Gemeldet - anzeigen

       
      Generell hast du recht, allerdings macht das nur Sinn, wenn die Ausführungszeit das rechtfertigt. 
      Während die Unterschiede eines Boolean zu einem Number-Filter sehr gering sind, haben Text-Filter eine mindestens 1000x längere Laufzeit, wenn man mehr als reines ASCII betrachtet. Natürlich würden Text-Filter in fast allen Fällen die Anzahl der Records massiv einschränken, aber die Ausführungszeit ist dennoch in der Praxis fast immer schlechter, wenn Text zuerst gefiltert wird.

      Wenn man mit einem Boolean-Filter schon mehr als die Hälfte der Records in einem einzigen Filterargument entfernen kann, dann muss der viel langsamere Text-Vergleich nicht mehr so viel durchsuchen. 

      In 95% aller "in der freien Wildbahn" gefundenen Filter haben wir eine Performance-Verbesserung durch die oben genannte Reihenfolge erzielen können. Es gibt ausnahmen, diese sind aber sehr selten und oft nur Aufgrund sehr spezieller Bedingungen anwendbar, daher habe ich diese in meiner Verallgemeinerung nicht genannt. 

      • Ninox-Professional
      • planoxpro
      • gestern
      • Gemeldet - anzeigen

       

      Okay, also insbesondere bei Textfiltern von Fall zu Fall schauen, welche Kombination aus Datentyp und Reihenfolge die beste Performance beim Filtern erwarten lässt.

Content aside

  • Status Answered
  • gesternZuletzt aktiv
  • 11Antworten
  • 90Ansichten
  • 6 Folge bereits