Neuigkeiten:

Ist euer Problem gelöst, dann bitte den Knopf "Thema gelöst" drücken!

Mobiles Hauptmenü

Mehrfach SELECT TOP (5)?

Begonnen von Holger69, September 17, 2023, 08:44:20

⏪ vorheriges - nächstes ⏩

Holger69

Guten Tag Zusammen,

ich betreibe eine Labordatenbank, welche die Messergebnisse von Kundenproben verwaltet und über ein Ticketsystem verarbeitet.

Jeder Kunde (CustomerID) kann 1:n Prozesse (ProzessID) haben
Jeder Kunde  kann 1:n Tickets (TicketID) haben.

Im Laufe der Zeit, schickt der Kunde mehrere Proben (SampleID) seiner Prozesse.
Immer dann, wenn er eine oder mehrere Proben schickt, entsteht ein Ticket. Das heißt, ein Ticket ist wie ein Container, in dem mehrere Proben zusammengefasst sind.

Ich hoffe, ich habe mich soweit verständlich ausgedrückt.

Jedes Ticket kann also 1:n Proben haben, wobei die jeweiligen SampleID's auch die dazugehörige ProzessID als Fremdschlüssel mit sich führen.

Für eine TicketID möchte ich nun die jeweils letzten 5 Proben der im Ticket enthaltenen Prozesse ermitteln.

Mit ,,WHERE ProzessID IN (SELECT ProzessID FROM tblSample WHERE TicketID = @TicketID)" als Kriterium, kann ich die über die im Ticket enthaltenen Prozesse ermitteln.

Gäbe es in einem Ticket stets nur eine Probe - also auch nur ein Prozess -, so würde
SELECT TOP (5) SampleID FROM tblSample ... + WHERE-Kriterium, wie oben formuliert,
das richtige Ergebnis liefern.

Da es aber im Ticket mehrere Proben/Prozesse gibt bzw. geben kann, müsste dieser SELECT TOP (5)-Befehl nacheinander für jede ProzessID ausgeführt werden, die durch die Unterabfrage ,,SELECT ProzessID from tblSample WHERE TicketID = @TicketID" geliefert wird.

Wie macht man das?
Muss man hier wirklich iterieren (und wenn ja, wie?) oder gibt es einen anderen Weg?

Vielen Dank im Voraus an alle, die sich damit beschäftigen möchten.

Gruß,
Holger
  •  

MzKlMu

Hallo,
das Beziehungsbild ist meist viel besser als die Beschreibung der Zusammenhänge.
Kannst Du bitte mal ein Bild des Beziehungsfensters hier zeigen ?
Gruß
Klaus
  •  

Beaker s.a.

Hallo,
ZitatIm Laufe der Zeit, schickt der Kunde mehrere Proben (SampleID) seiner Prozesse.
Immer dann, wenn er eine oder mehrere Proben schickt, entsteht ein Ticket.
D.h. die Tickets sind abhängig vom Prozess, nicht vom Kunden. Der ergibt sich
ja schon aus dem Prozess.
Aber, wie schon gewünscht, ein Bild des Beziehungsfensters sollte her.

gruss ekkehard
--
Frauen aller Länder vereinigt euch! Wir brauchen eine Wiedergeburt des Matriarchats.
Und schickt den Papst in die Wüste! Da kann er 40 Tage auf God(o)t warten.
  •  

Holger69

Vielen Dank erst mal für die Hinweise.

Ich habe das Beziehungsfenster in Access nachgestellt, wenngleich nicht alle Felder der einzelnen Tabellen enthalten sind.

Die tblSample enthält die Fremdschlüssel aus tblProcess und tblTicket.

Ich habe auch die tblSample mit Beispieldaten angehängt (nur die Schlüssel, der Rest ist für die Fragestellung ja unerheblich)

Die Farben in den Beispieldaten beziehen sich jeweils auf die gleiche ProzessID und sollen einfach nur zeigen, welche Proben zum gleichen Prozess gehören.

In meinem Beispiel schickt der Kunde  - sagen wir einmal im Monat - jeweils eine Probe von seinen 3 Prozessen. Eine solche Sendung wird in einem Ticket zusammengefasst. Im Beispiel schickt er pro Ticket immer Proben von allen drei Prozessen AUSSER bei dem Ticket Nr. 5, da hat er nur die Probe des Prozess Nr. 2 geschickt. (Siehe "!")

Farblich markiert sind nur jeweils die letzten 5 SampleID's eines Prozesses, ausgehend von Ticket Nr. 9 bzw. Ticket Nr. 7.

Das von mir benötigte Abfrageergebnis soll am Ende alle farbigen SampleID's liefern, also in "halb -SQL":
TOP (5) SampleID für Prozess 1 + TOP (5) SampleID für Prozess 2 + TOP (5) SampleID für Prozess 3

Gezählt wird von der vorgegebenen Ticketnummer, welche als Variable der Abfrage mitgegeben wird. Im Beispiel 9 bzw. 7

Das Ganze dient dazu, dem Kunden die Historie der letzten 5 Proben seiner Prozesse zu liefern.

Ich hoffe, ich konnte es nun etwas deutlicher beschreiben.
  •  

ebs17

Deine Datensatzdarstellung verstehe ich nicht. Wenn ich auf ProzessID = 9 filtere, gibt es dann nur Datensätze mit 9.

Aber nein, da braucht man keine Schleife und Iteration.
TOP X macht praktisch nur Sinn, wenn man eine Sortierung beifügt, es sei denn, man ist da dem Zufall zugeneigt.

Allgemeine Demonstration: Die folgende Abfrage zeigt aus der Tabelle pro TickedID 5 Datensätze mit dem höchsten Datum an, unter dem Vorbehalt, dass es da 5 Datensätze gibt.
Bei vielen Datensätzen mit gleichem Datum  wird als aussortierendes eindeutiges Kriterium die Sortierung nach der ID verwendet, also die letzten Eintrage. Das zweite Kriterium ist etwas willkürlich, aber wer weiß, was er will, kann das sinngemäß ändern.
SELECT
   T.*
FROM
   tblSample AS T
WHERE
   T.SampleID IN
      (
         SELECT TOP 5
            X.SampleID
         FROM
            tblSample AS X
         WHERE
            X.TicketID = T.TicketID
         ORDER BY
            X.SampleDate DESC,
            X.SampleID DESC
      )
Mit freundlichem Glück Auf!

Eberhard
  •  

Holger69

Vielen Dank Eberhard, für deine Antwort.

Ich glaube, es ist noch nicht klar, was ich eigentlich haben möchte.

SELECT TOP (5) SampleID FROM tblSample WHERE ProcessID = ProcessID

würde mir nur für EINEN Prozess die letzten 5 SampleID's liefern.
Aber innerhalb einer TicketID kann es mehrere Prozesse geben. Welche das sind, ermittle ich entweder über

SELECT ProcessID FROM tblSample WHERE TicketID = TicketID)

oder innerhalb eines WHERE-Kriteriums mit

WHERE ProcessID in (SELECT ProcessID FROM tblSample WHERE TicketID = TicketID

In meinem Beispiel (TicketID = 9) liefert dies die ProzessID's 1, 2 und 3.
Um nun für diese drei Prozesse die JEWEILS letzten 5 SampleID's zu ermitteln, wären drei Abfragen nacheinander nötig:

SELECT TOP (5) SampleID FROM tblSample WHERE ProcessID = 1
SELECT TOP (5) SampleID FROM tblSample WHERE ProcessID = 2
SELECT TOP (5) SampleID FROM tblSample WHERE ProcessID = 3

Sofern es für jeden Prozess 5 SampleID's gibt, müssen nun 15 SampleID's als Abfrageergebnis herauskommen. Und diese 15 SampleID's sind das, was ich will.

Aber wie kann ich diese Abfragen zu einem SELECT-String kombinieren?

  •  

markusxy

Wenn es wirklich ein MS SQL Server ist würde ich es so machen:

SELECT Y.*
FROM tblSample B
CROSS APPLY (
SELECT TOP 5 *
FROM tblSample
WHERE ProcessID = B.ProcessID
ORDER BY SampleDate DESC
) Y
WHERE B.TicketID = @TicketID

Falls du Apply nicht kennst einfach mal die verschiedenen Versionen in der Hilfe ansehen.
Im Prinzip vergleichbar mit einem korrelierenden Subselect.
  •  

ebs17

ZitatSELECT TOP (5) SampleID FROM tblSample WHERE ProcessID = ProcessID

würde mir nur für EINEN Prozess die letzten 5 SampleID's liefern.
Woraus nimmst Du diesen Glauben an die "letzten"?

Zu meinem Vorschlag: Wenn Du pro Ticket und Prozess 5 Datensätze brauchst, wäre die zweite ID in die Korrelation einzubeziehen.
Mit freundlichem Glück Auf!

Eberhard
  •  

markusxy

#8
@ebs17, die Logik in deiner Abfrage stimmt nicht. Die Verknüpfung über das Ticket bedeutet, doch dass bei allen Tickets die letzten fünf Proben zurückgegeben werden - was aber nichts mit dem Problem zu tun hat.

Es funktioniert natürlich auch mit deinem Vorschlag sobald die Logik angepasst ist.
  •  

ebs17

Da scheinst Du recht zu haben. Vermutlich müsste da die zweite TOP_X als Unterabfrage geschachtelt werden. womit eine Performance gnadenlos erschossen wird.
Mit freundlichem Glück Auf!

Eberhard
  •  

Holger69

Herzlichen Dank, markusxy!
CROSS APPLY war mir wirklich noch nicht bekannt. Das hat mich wirklich weiter gebracht!
Deinen Code konnte ich so übernehmen, wie er war ;)

Und ja, ich gebe zu, dass ich ein ORDER BY über das SampleDate vernachlässigt habe. Aber es ändert jetzt auch nichts Grundsätzliches an der Fragestellung, wie man mehrere SELECT TOP (X) in eine Abfrage packt.

Jetzt nur noch eins:
Dieser gezeigte CROSS APPLY liefert immer die letzten bzw. neuesten 5, so wie sie aktuell sind egal, welche TicketID ich eingebe.
Ich suche zusätzlich die Möglichkeit, mit einer kleineren (älteren) TicketID zu einem früheren Zeitpunkt zu gehen, um die Konstellation zum damaligen Zeitpunkt abzufragen. D.h. im Prinzip so, als gäbe es noch gar keine neueren SampleID's bzw TicketID's.
  •  

markusxy

#11
@ebs, habs mal kurz getestet, so klappt es:

DECLARE @Sample AS Table(ID Int identity, ProcessID Int, TicketID Int, SampleDate Date)

Insert into @Sample Values(1,1,'20230101'), (2,1,'20230101'), (1,2,'20230103'), (2,2,'20230103'), (1,3,'20230105'), (2,3,'20230105'), (5,5,'20230110');

SELECT S.*
FROM @Sample S
INNER JOIN (
   SELECT ProcessID
   FROM @sample
   WHERE TicketID = 2
   ) X ON X.ProcessID = S.ProcessID
WHERE s.id IN (
      SELECT TOP 2 ID
      FROM @sample
      WHERE ProcessID = S.ProcessID
      ORDER BY SampleDate DESC
      )

Wobei die Performance hier jetzt nicht grob leidet.
Aber Apply ist von der Umsetzung her weit effizienter.
  •  

markusxy

Zitat von: Holger69 am September 18, 2023, 10:03:56Ich suche zusätzlich die Möglichkeit, mit einer kleineren (älteren) TicketID zu einem früheren Zeitpunkt zu gehen, um die Konstellation zum damaligen Zeitpunkt abzufragen. D.h. im Prinzip so, als gäbe es noch gar keine neueren SampleID's bzw TicketID's.

Wenn du die Logik verstanden hast, solltest du das jetzt problemlos umsetzen können.
Du brauchst ja nur die zusätzliche Bedingung aufnehmen. ;)
  •