SQL Filtern von doppelten Werten

Genau das ist doch sein Problem...
Er möchte die Artikelnummer genau 1x haben und bekommt dieses nicht hin. Erster Ansatz ist Group By, welcher aber erstmal das Resultset in den Speicher lädt um dann das Aggregat zu erstellen. Besser ist eben auf einem kleinen Recordset die Duplikate zu entfernen und dann die benötigten Informationen aus den umliegenden Tabellen heranzuholen.

Group By liefert kein falsches Ergebnis, aber die Aufwand für die Maschine ist größer.
 
Es war ein Beispiel, und dann auch noch ein simples. :)

Window kann alles was Group by auch kann, sogar teils mehr, weil nicht alle Aggregate (group by) auch Windowfunktionen sind und umgekehrt ebenso.

Man kann auch sowas wie SELECT DISTINCT MIN(artikel_id) over (partition by artikel_name) AS artikel_id sagen und erhält so eine Liste von eindeutigen IDs auch dann, wenn diese Zuordnung in der ursprünglichen Tabelle eben nicht eindeutig war.

Welche Windowfunktion paßt, muß man natürlich gucken, ebenso wie darauf, ob man das Ergebnis noch weiterverarbeiten muß.

GROUP BYs mit X vielen toten Spalten bringt aber in 99 von 50 Fällen nichts, außer daß man nicht durchsieht.
 
Hi,

ok, sehe das Problem aktuell zwar immer noch nicht ganz, aber vielen lieben Dank für die Erläuterung und die Zeit dafür! Daumen hoch!

VG,
Mad
 
  • Gefällt mir
Reaktionen: parats
Ich geb gerne zu, daß ich bei dem gelisteten Schema nicht ganz durchseh. 😊 Dazu müßte ich das wohl erstmal nachbauen und mit ein paar Daten füttern, wozu mir grad etwas die Motivation fehlt.

Mir geht es einfach darum, daß man seine Abfrage so komplex wie nötig, so einfach wie möglich gestaltet. Klar kommt noch ein bißchen was dazu, aber das ist der Kern: so wenig Daten anfassen wie unbedingt erforderlich und unnötige Berechnungen vermeiden, ggfs mit Kompromiß wenn sonst die Abfrage zu unübersichtlich/fehlerträchtig wird.

Und wenn ich 100 Spalten im Group by hab (dann auch noch per Position) muß ich mich fragen, ob ich die gesamte Abfrage nicht besser neu entwerfen sollte.

Ansatz muß sein, so wenig Daten wie möglich anzufassen. Verzierungen wie "ja welcher Artikel/Kunde/xyz ist denn nun der mit dieser ID?" sind zunächst irrelevant, es geht nur um die Struktur und um die erforderliche Zuordnung Artikel;Kunde;Lieferdatum. Diese Zuordnung muß auch eindeutig sein (ohne Dopplungen).
Wenn man das hat, dann kann man alles andere anhängen, und dann sollten Aggregate wie Windowfunktionen unnötig werden.
 
Vielen Dank für die zahlreichen Antworten und Erklärungen!
Und ich finds klasse, wie sich hier einen Eigendynamik entwickelt hat :)

So ganz weiß ich nur gerade leider nicht, wie ich meine Abfrage jetzt anpassen kann, um die Window Funktionen zu nutzen.
Kann mir da jemand helfen?
Code:
select
K.KUNDEN_NR
, K.NAME1
, K.NAME2
, K.STRASSE
, K.LAND
, K.PLZ
, K.ORT
, G.ARTGRUPPE as Warengruppe
, G.BEZEICHNUN as WG_Bezeichnung
, A.ARTIKEL_NR as EDV_Nummer
, L.K_ATNR as Kundenartikel_NR
, L.ARTBEZ1
, L.ARTBEZ2
, L.ARTBEZ3
, AK.FELD_C1 as Artikelbezeichnung4
, AK.FELD_C2 as Artikelbezeichnung5
, AK.FELD_C3 as Artikelbezeichnung6
, A.HERKUNFT_1 as Zolltarifnummer
, I.BEZEICH1 as Bezeichnungs_Zolltarif
, MAX(LIEF_DATUM) as Lieferdatum
from LFSCHEIN L
INNER JOIN ARTIKELC A on L.ARTIKELNR = A.ARTIKEL_NR
JOIN ART_AKA AK on A.ARTIKEL_NR = AK.ARTIKEL_NR
JOIN KARTIKEL KA on A.ARTIKEL_NR=KA.ARTIKEL_NR
JOIN GRUPPEC G on A.ARTGRUPPE = G.ARTGRUPPE
JOIN KUNDENA K on KA.KUNDEN_NR = K.KUNDEN_NR
JOIN IM_STAMM I on A.HERKUNFT_1 = I.KENNUNG
where L.LIEF_DATUM>='01/01/2020'
and K.KUNDEN_NR >30000
GROUP by K.KUNDEN_NR, K.NAME1, K.NAME2, K.STRASSE, K.LAND, K.PLZ, K.ORT, G.ARTGRUPPE, G.BEZEICHNUN, A.ARTIKEL_NR, L.K_ATNR, L.ARTBEZ1, L.ARTBEZ2, L.ARTBEZ3, AK.FELD_C1, AK.FELD_C2, AK.FELD_C3, A.HERKUNFT_1, I.BEZEICH1
HAVING (COUNT(A.ARTIKEL_NR)=1)
order by K.KUNDEN_NR
 
  • join und inner join ist dasselbe
  • du schmeißt das GROUP BY da unten ersatzlos raus
  • du guckst nach wie die Dopplungen beschaffen sind - kommt erwartungsgemäß aus einem JOIN rein
  • du setzt dort, wo die Dopplungen herkommen, statt des GROUP BY eine Windowfunktion nach dem Schema
SQL:
select MIN(pk) OVER (order by spalte_n_mit_Dopplungen) new_pk FROM relation mit den Dopplungen -- kann auch ein JOIN sein
jetzt hast Du in dieser Relation dopplungsfreie Einträge und kannst diese mit dem Rest verjoinen.
 
Ich muss gestehen, dass ich nicht 100%ig verstehe, was du wirklich machen willst, denn wie du im Eingangspost schreibst, willst du alle an einen bestimmten Kunden gelieferten Artikel jeweils ein Mal anzeigen.
Dein Query selektiert allerdings nur die Artikel, die lediglich ein einziges Mal an den betroffenen Kunden im entsprechenden Zeitraum geliefert wurden: HAVING (COUNT(A.ARTIKEL_NR)=1)).

Ich gehe aber davon aus, dass du das machen willst, was du textuell beschrieben hast.
Wenn nicht, ist das Folgende nicht unbedingt relevant...


Window Functions sind mit Vorsicht zu genießen.
Wenn du keine Ressourcenprobleme auf deinen Datenbanken hast, oder dir die Zeitpunkte mit wenig Last rauspicken kannst, ist es egal, wie du deine SQLs formulierst, aber wenn du im produktiven Betrieb eines potentiell notorisch IT-unterfinanziernenden Unternehmens laufen musst, oder es zu jeder Zeit auf Performance ankommt, lass die Finger von überladenen Lösungen.

Um es möglichst kurz zu beschreiben:
Normale Selects gehen iterativ über die gewünschte Datenmenge und beschränken jegliche Operationen und Berechnungen auf die aktuelle Iteration, also den aktuellen Datensatz und schließen damit das Einbeziehen von Informationen außerhalb des aktuellen Datensatzes kategorisch aus.

Ein Select mit Group By geht ebenso iterativ über die gewünschte Datenmenge, jedoch mit dem Unterschied, dass die einzelnen Iterationen nach gewissen Kriterien gruppiert werden können und innerhalb der Gruppierungen Daten über Aggregationsfunktionen zusammengefasst werden können.

Beide landen nach O-Notation bei O(n), wobei das Select mit Group By natürlich aber etwas Zusatzarbeit und -ressourcen aufruft und u.U. stärker von Indizes bzw. vom Caching der aktuellen Tabelle im Puffer abhängt.

Ein Order By erhöht den Aufwand entsprechend, da die Datenmenge am Ende noch sortiert werden muss.
Der Aufwand steigt dann auf O(n) + O(n²) im Worst-Case und ist sehr stark abhängig vom Vorhandensein von Indizes für die Sortierkriterien bzw. vom Caching-Zustand der Tabelle.

Ein Select mit einer Window Function läuft nicht mehr iterativ ab.
Mit einer Window Function hast du, neben dem aktuellen Datensatz, Zugriff auf Informationen aus dem jeweilig vorhergehenden und nachfolgenden Datensatz.
D.h. nun, dass, egal was du machst, dieser Select implizit immer mit einem Order By ausgeführt wird, da sonst nicht die Möglichkeit bestünde, zu beurteilen, welcher Datensatz vor oder nach dem aktuellen steht.
Der Aufwand ist also immer mindestens O(2n) + O(n²) für einfache Window Functions bzw. O(3n) + O(n²) wenn auf Vorgänger und Nachfolger zugegriffen wird.
Ein Order By auf das Endergebnis erhöht den Aufwand entsprechend noch einmal um O(n²).

Eine Window Function ist in deinem Fall, zumindest, wie ich ihn verstehe, einfach nur Overkill und bringt zudem keinen erweiterten Nutzen.


Mein Tipp für solche Fälle: Select kann nicht einfach nur Daten aus Tabellen selektieren, sondern auch aus Views jeder Art und, hier besonders relevant, aus anderen Selects!
D.h. du kannst zuerst ein Select formulieren, in dem nur die notwendigsten Daten bezogen werden, und die darüber selektierte Datenmenge wie einen normalen Table joinen:
SQL:
select /*Was auch immer*/
.
.
.
from ARTIKELC A
INNER JOIN (select distinct L.ARTIKELNR as ANR
            from LFSCHEIN L
            INNER JOIN KARTIKEL KA on KA.ARTIKEL_NR = L.ARTIKELNR
            INNER JOIN KUNDENA K on K.KUNDEN_NR = KA.KUNDEN_NR
            where K.KUNDEN_NR = 30002
              AND L.LIEF_DATUM >= '01/01/2020') on ANR = A.ARTIKEL_NR
INNER JOIN \*BlaBlub*\ on \*Kriteria*\
.
.
.
order by L.ARTIKELNR

Besonders nützlich ist dieser Ansatz auch, wenn tatsächlich eine Gruppierung der Daten vollzogen werden muss und dabei aber auch Informationen einbezogen werden sollen, die schlicht nicht aggregiert werden können (z.B. Blobs o.ä.):
SQL:
select A.ARTIKEL_NR, CANR, A.BlobField
       /*Was auch immer*/
.
.
.
from ARTIKELC A
INNER JOIN (select L.ARTIKELNR as ANR, Count(L.ARTIKELNR) as CANR
            from LFSCHEIN L
            INNER JOIN KARTIKEL KA on KA.ARTIKEL_NR = L.ARTIKELNR
            INNER JOIN KUNDENA K on K.KUNDEN_NR = KA.KUNDEN_NR
            where K.KUNDEN_NR = 30002
              AND L.LIEF_DATUM >= '01/01/2020'
            GROUP BY L.ARTIKELNR) on ANR = A.ARTIKEL_NR
INNER JOIN \*BlaBlub*\ on \*Kriteria*\
.
.
.
order by L.ARTIKELNR
 
  • Gefällt mir
Reaktionen: Enurian
Zurück
Oben