Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Abfrage: Kund hat Artikel X aber nicht Y gekauft (https://www.delphipraxis.net/214938-abfrage-kund-hat-artikel-x-aber-nicht-y-gekauft.html)

BlueStarHH 8. Apr 2024 11:34

Datenbank: Firebird • Version: 3.x • Zugriff über: IBDAC

Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Hallo,

ich möchte alle Kunden haben, die den Artikel X gekauft haben und nie den Artikel Y gekauft haben. (Hinweis die Artikel X und Y können in verschiedenen Rechnungen des selben Kunden auftreten). Die Tabellen sehen so aus:


Code:
Kunden
------
KdNr, Name, ...

Rechnungen
-----------
KdNr, ReNr, ....

Positionen
----------
ReNr, Artikel, ....
Danke!

Delphi.Narium 8. Apr 2024 12:04

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Syntaktisch sehr altbackene Version:
SQL-Code:
select k1.*
from  kunden k1, rechnungen r1, positionen p1
where k1.kdnr   = r1.kdnr
and   r1.renr   = p1.renr
and   p1.artikel = 'X'
and not exists

  select 1
  from  kunden k2, rechnungen r2, positionen p2
  where k2.kdnr   = r2.kdnr
  and   r2.renr   = p2.renr
  and   p2.artikel = 'Y'
  and   k1.kdnr   = k2.kdnr
)

BlueStarHH 8. Apr 2024 12:19

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Danke, das klappt!

Delphi.Narium 8. Apr 2024 12:32

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Noch ein Versuch:
SQL-Code:
select kdnr
from (
  select
    kdnr,
    Sum(HatX) as HatX, /* Wie oft kommt der Artikel X vor? */
    Sum(HatY) as HatY /* Wie oft kommt der Artikel Y vor? */
  from (
    select
      k1.kdnr,
      case when p1.artikel = 'X' then 1 else 0 end as HatX, /* Jeder Artikel X zählt. */
      case when p1.artikel = 'Y' then 1 else 0 end as HatY /* Jeder Artikel Y zählt. */
    from  kunden k1, rechnungen r1, positionen p1
    where k1.kdnr = r1.kdnr
    and   r1.renr = p1.renr
    and   p1.artikel in ('X','Y') /* Alle Kunden, die irgendwo Artikel X oder Y erworben haben. */
  )
  group by kdnr
)
where HatY = 0; /* Nur die, bei denen Artikel Y nicht vorkommt. */

Uwe Raabe 8. Apr 2024 12:54

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Das müsste doch aber auch mit Joins anstatt Sub-Selects funktionieren.

So aus dem Ärmel, ohne das hier testen zu können:
SQL-Code:
select k1.*
from kunden k1
join rechnungen r1 on r1.kdnr = k1.kdnr
join positionen p1 on p1.renr = r1.renr and p1.artikel = 'X'
join positionen p2 on p2.renr = r1.renr and p2.artikel = 'Y'
having (Count(p1.artiklel) > 0) and (Count(p2.artikel) = 0)

gubbe 9. Apr 2024 14:53

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1535507)
Das müsste doch aber auch mit Joins anstatt Sub-Selects funktionieren.

So aus dem Ärmel, ohne das hier testen zu können:
SQL-Code:
select k1.*
from kunden k1
join rechnungen r1 on r1.kdnr = k1.kdnr
join positionen p1 on p1.renr = r1.renr and p1.artikel = 'X'
join positionen p2 on p2.renr = r1.renr and p2.artikel = 'Y'
having (Count(p1.artiklel) > 0) and (Count(p2.artikel) = 0)

Guter Ansatz, aber da würde mindestens noch ein "group by" fehlen, sonst lässt sich nichts zählen und "having" allein ergibt keinen Sinn.

Uwe Raabe 9. Apr 2024 15:04

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Zitat:

Zitat von gubbe (Beitrag 1535525)
Guter Ansatz, aber da würde mindestens noch ein "group by" fehlen, sonst lässt sich nichts zählen und "having" allein ergibt keinen Sinn.

In der Tat, die Zeile ist mir beim Übertragen wohl verloren gegangen.

BlueStarHH 9. Apr 2024 15:12

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1535527)
Zitat:

Zitat von gubbe (Beitrag 1535525)
Guter Ansatz, aber da würde mindestens noch ein "group by" fehlen, sonst lässt sich nichts zählen und "having" allein ergibt keinen Sinn.

In der Tat, die Zeile ist mir beim Übertragen wohl verloren gegangen.

Aber selbst damit klappt es merkwürdiger weise nicht. Ergebnis ist komplett leer, obwohl das SQL OK aussieht.
Druch das "group by" ist auch kein "select k1.*" mehr möglich.

Uwe Raabe 9. Apr 2024 15:18

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Ich würde das ja auch lieber an realen Daten ausprobieren. In der Theorie sieht ja vieles einfacher aus als es hinterher in der Praxis ist.

Delphi.Narium 9. Apr 2024 15:42

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Wenn bei dem SQL ein Kunde keinen Artikel Y gekauft hat, ist die Ergebnismenge leer, egal wieviele X er gekauft hat.

Wenn ein Kunde zwar Artikel Y gekauft hat, aber keinen Artikel X ist die Ergebnismenge ebenfalls leer. Gut, die Kunden interessieren uns nicht.

Nur wenn ein Kunde X und Y gekauft hat und diese sich in der gleichen Rechnung befinden, wird die Ergebnismenge nicht leer sein. Aber sie enthält dann nur Kunden, die wir nicht haben wollen.

Positionen p2 muss über eine eigene Rechnung mit den Kunden verbunden werden, um sicherzustellen, dass Artikel X und Artikel Y nicht in einer Rechnung erscheinen müssen.
SQL-Code:
select k1.kdnr
from kunden k1
join rechnungen r1 on r1.kdnr = k1.kdnr
join positionen p1 on p1.renr = r1.renr and p1.artikel = 'X'
join rechnungen r2 on r2.kdnr = k1.kdnr
join positionen p2 on p2.renr = r2.renr and p2.artikel = 'Y'
having (Count(p1.artiklel) > 0) and (Count(p2.artikel) = 0)
Auch das wird noch nicht funktionieren, Frage ist, wo genau muss hier ein Left vor den Join und wie kann man dann sicherstellen, dass wirklich nur die Kunden erscheinen, die Artikel X erworben haben? Momentan hab' ich dafür absolut keine Idee. Bin mir nicht sicher, ob ein Left vor Positionen p2 ausreicht oder doch irgendwelche unerwünschten Nebenwirkungen hat. Ohne Daten und entsprechende Tabellen ist das halt doch eher Gehirnjogging mit großem Ratefaktor, als fundierte Lösungsfindung.

Aggregatfunktionen benötigen immer ein Group by. Select * funktioniert bei Group by nie. Alles, was im Select aufgeführt wird, muss entweder im Group By stehen oder als Aggregatfunktion im Select erscheinen.

jaenicke 9. Apr 2024 17:20

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Wie wäre es so?
SQL-Code:
SELECT DISTINCT K.*
FROM Kunden K
INNER JOIN Rechnungen R ON K.KdNr = R.KdNr
INNER JOIN Positionen P1 ON R.ReNr = P1.ReNr AND P1.Artikel = A
LEFT JOIN Positionen P2 ON R.ReNr = P2.ReNr AND P2.Artikel = B
WHERE P2.ReNr IS NULL;
Eine Zählung ist ja nicht notwendig. Es geht ja nur um die Existenz.

Testcode für https://sqliteonline.com/
SQL-Code:
create table Kunden (
    KdNr int,
    Name varchar(255)
);

create table Rechnungen (
    KdNr int,
    ReNr int
);

create table Positionen (
    ReNr int,
    Artikel int
);

insert into Kunden (KdNr, Name) VALUES (1, 'Meier');
insert into Kunden (KdNr, Name) VALUES (2, 'Schulz');

insert into Rechnungen (KdNr, ReNr) VALUES (1, 100);
insert into Rechnungen (KdNr, ReNr) VALUES (2, 200);

insert into Positionen (ReNr, Artikel) VALUES (100, 4701);
insert into Positionen (ReNr, Artikel) VALUES (100, 4702);
insert into Positionen (ReNr, Artikel) VALUES (200, 4701);
insert into Positionen (ReNr, Artikel) VALUES (200, 5702);


SELECT DISTINCT K.*
FROM Kunden K
INNER JOIN Rechnungen R ON K.KdNr = R.KdNr
INNER JOIN Positionen P1 ON R.ReNr = P1.ReNr AND P1.Artikel = 4701
LEFT JOIN Positionen P2 ON R.ReNr = P2.ReNr AND P2.Artikel = 5702
WHERE P2.ReNr IS NULL;

Jasocul 10. Apr 2024 05:50

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Zitat:

Zitat von BlueStarHH (Beitrag 1535528)
Zitat:

Zitat von Uwe Raabe (Beitrag 1535527)
Zitat:

Zitat von gubbe (Beitrag 1535525)
Guter Ansatz, aber da würde mindestens noch ein "group by" fehlen, sonst lässt sich nichts zählen und "having" allein ergibt keinen Sinn.

In der Tat, die Zeile ist mir beim Übertragen wohl verloren gegangen.

Aber selbst damit klappt es merkwürdiger weise nicht. Ergebnis ist komplett leer, obwohl das SQL OK aussieht.
Druch das "group by" ist auch kein "select k1.*" mehr möglich.

Natürlich ist das Ergebnis leer. Da sollte ein "outer join" helfen.
Code:
select k2.*
from (
select k1.kdnr
from kunden k1
join rechnungen r1 on r1.kdnr = k1.kdnr
join positionen p1 on p1.renr = r1.renr and p1.artikel = 'X'
left outer join positionen p2 on p2.renr = r1.renr and p2.artikel = 'Y'
group by k1.kdnr
having (Count(p1.artiklel) > 0) and (Count(p2.artikel) = 0)) as Treffer
join kunden k2 on k2.kdnr = Treffer.kdnr
oder
Code:
select k2.*
from kunden k2
where k2.kdnr in (
select k1.kdnr
from kunden k1
join rechnungen r1 on r1.kdnr = k1.kdnr
join positionen p1 on p1.renr = r1.renr and p1.artikel = 'X'
left outer join positionen p2 on p2.renr = r1.renr and p2.artikel = 'Y'
group by k1.kdnr
having (Count(p1.artiklel) > 0) and (Count(p2.artikel) = 0))
Es gibt meines Wissens aber DBs, die eine Begrenzung bei der Anzahl im Ergebnis von "in" haben. Vermutlich ist die zweite Variante bei großen Datenmengen auch langsamer.
So müssten auch alle Kundenfelder verfügbar sein.
Es gibt auch noch andere Lösungen. Man könnte z.B. mit "with" arbeiten oder auf der DB passende Views erstellen.

Die SQL sind ungetestet. Könnte also sein, dass das nicht auf Anhieb funktioniert.

IBExpert 10. Apr 2024 06:22

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
ich finde sowas einfacher zu verstehen und modularer, weil auch mit anderen bedingungen einfach zu kombinieren und trotzdem
die unterbedingungen einfach lesbar sind.

Code:
SELECT DISTINCT K.*
FROM Kunden K
where
exists(select * from Rechnungen R JOIN Positionen P1 ON R.ReNr = P1.ReNr AND P1.Artikel = 4701 where K.KdNr = R.KdNr)
and
not exists(select * from Rechnungen R JOIN Positionen P1 ON R.ReNr = P1.ReNr AND P1.Artikel = 5702 where K.KdNr = R.KdNr)

Uwe Raabe 10. Apr 2024 10:14

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Zitat:

Zitat von jaenicke (Beitrag 1535538)
SQL-Code:
SELECT DISTINCT K.*
FROM Kunden K
INNER JOIN Rechnungen R ON K.KdNr = R.KdNr
INNER JOIN Positionen P1 ON R.ReNr = P1.ReNr AND P1.Artikel = A
LEFT JOIN Positionen P2 ON R.ReNr = P2.ReNr AND P2.Artikel = B
WHERE P2.ReNr IS NULL;

So nur vom drüberlesen: Fallen da nicht alle Rechnungen raus, bei denen kein A aber ein B enthalten sind?

jaenicke 10. Apr 2024 11:12

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1535561)
So nur vom drüberlesen: Fallen da nicht alle Rechnungen raus, bei denen kein A aber ein B enthalten sind?

Das ist ja auch die Anforderung. Es sollen alle Kunden, die Artikel A gekauft haben, drin sein, und von denen nur die, die B nicht gekauft haben.

Man könnte auch nur LEFT JOINs verwenden, aber sofern die Tabellen indiziert sind, sollte es mit dem INNER JOIN schneller sein. Beim dritten JOIN muss wiederum ein LEFT JOIN verwendet werden, damit die Nullwerte erhalten bleiben.

Uwe Raabe 10. Apr 2024 11:34

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Zitat:

Zitat von jaenicke (Beitrag 1535566)
Das ist ja auch die Anforderung. Es sollen alle Kunden, die Artikel A gekauft haben, drin sein, und von denen nur die, die B nicht gekauft haben.

Das ist mir schon klar, aber mit
Code:
INNER JOIN Positionen P1 ON R.ReNr = P1.ReNr AND P1.Artikel = A
werden ja nur Rechnungen selektiert, bei denen mindestens ein A in den Positionen vorkommt. Damit werden Rechnungen, bei denen kein A aber ein B vorkommt nicht berücksichtigt. Wenn also ein Kunde eine Rechnung nur mit A und eine nur mit B hat, würde er fälschlicherweise in die Ergebnismenge aufgenommen. Oder sehe ich das falsch?

jaenicke 10. Apr 2024 11:53

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Nein, denn wie du sagst, die Kunden mit B fliegen in der Zeile ja schon raus.

Uwe Raabe 10. Apr 2024 12:01

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Hmmm, ich habe mal dein Beispiel entsprechend ergänzt:
Zitat:

Zitat von SQL
create table Kunden (
KdNr int,
Name varchar(255)
);

create table Rechnungen (
KdNr int,
ReNr int
);

create table Positionen (
ReNr int,
Artikel int
);

insert into Kunden (KdNr, Name) VALUES (1, 'Meier');
insert into Kunden (KdNr, Name) VALUES (2, 'Schulz');

insert into Rechnungen (KdNr, ReNr) VALUES (1, 100);
insert into Rechnungen (KdNr, ReNr) VALUES (2, 200);
insert into Rechnungen (KdNr, ReNr) VALUES (1, 300);

insert into Positionen (ReNr, Artikel) VALUES (100, 4701);
insert into Positionen (ReNr, Artikel) VALUES (100, 4702);
insert into Positionen (ReNr, Artikel) VALUES (200, 4701);
insert into Positionen (ReNr, Artikel) VALUES (200, 5702);
insert into Positionen (ReNr, Artikel) VALUES (300, 5702);


SELECT DISTINCT K.*
FROM Kunden K
INNER JOIN Rechnungen R ON K.KdNr = R.KdNr
INNER JOIN Positionen P1 ON R.ReNr = P1.ReNr AND P1.Artikel = 4701
LEFT JOIN Positionen P2 ON R.ReNr = P2.ReNr AND P2.Artikel = 5702
WHERE P2.ReNr IS NULL;

Kunde 1 (Meier) hat nun Rechnung 100 mit 4701 und Rechnung 300 mit 5702, hat also A und B gekauft. Trotzdem wird er im Ergebnis angezeigt.

jaenicke 10. Apr 2024 12:25

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Ja, da hast du Recht. Mit einem join in der Form sollte es aber gehen:
SQL-Code:
SELECT DISTINCT K.KdNr, K.Name
FROM Kunden K
INNER JOIN Rechnungen R ON K.KdNr = R.KdNr
INNER JOIN Positionen PA ON R.ReNr = PA.ReNr AND PA.Artikel = 4701
LEFT JOIN (
    SELECT DISTINCT R.KdNr
    FROM Rechnungen R
    INNER JOIN Positionen PB ON R.ReNr = PB.ReNr
    WHERE PB.Artikel = 5702
) AS KundenMitArtikelB ON K.KdNr = KundenMitArtikelB.KdNr
WHERE KundenMitArtikelB.KdNr IS NULL;

Delphi.Narium 10. Apr 2024 13:05

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Aber damit ist die Vermutung widerlegt:
Zitat:

Zitat von Uwe Raabe
Das müsste doch aber auch mit Joins anstatt Sub-Selects funktionieren.

'ne weitere Alternative, auch mit 'nem Subselect:
SQL-Code:
create table Kunden (
KdNr int,
Name varchar(255)
);

create table Rechnungen (
KdNr int,
ReNr int
);

create table Positionen (
ReNr int,
Artikel int
);

insert into Kunden    (KdNr, Name)   VALUES (  1, 'Meier');
insert into Rechnungen (KdNr, ReNr)   VALUES (  1, 100);
insert into Rechnungen (KdNr, ReNr)   VALUES (  1, 101);
insert into Positionen (ReNr, Artikel) VALUES (100, 4701);
insert into Positionen (ReNr, Artikel) VALUES (100, 4702);
insert into Positionen (ReNr, Artikel) VALUES (101, 5702);

insert into Kunden    (KdNr, Name)   VALUES (  2, 'Schulz');
insert into Rechnungen (KdNr, ReNr)   VALUES (  2, 200);
insert into Positionen (ReNr, Artikel) VALUES (200, 4701);
insert into Positionen (ReNr, Artikel) VALUES (200, 5702);

insert into Kunden    (KdNr, Name)   VALUES (  3, 'Gärtner');
insert into Rechnungen (KdNr, ReNr)   VALUES (  3, 300);
insert into Positionen (ReNr, Artikel) VALUES (300, 4701);

insert into Kunden    (KdNr, Name)   VALUES (  4, 'Bauer');
insert into Rechnungen (KdNr, ReNr)   VALUES (  4, 400);
insert into Positionen (ReNr, Artikel) VALUES (400, 4702);

commit;

select kdnr, name
from (
  select K.kdnr, k.name,
  sum(case when Coalesce(p1.ReNr,0) <> 0 then 1 else 0 end) as HatX,
  sum(case when Coalesce(p2.ReNr,0) <> 0 then 1 else 0 end) as HatY
  from Kunden K
  inner join Rechnungen R1 on K.KdNr = R1.KdNr
  left join Positionen P1 on R1.ReNr = P1.ReNr and P1.Artikel = 4701
  inner join Rechnungen R2 on K.KdNr = R2.KdNr
  left join Positionen P2 on R2.ReNr = P2.ReNr and P2.Artikel = 5702
  group by
    k.kdnr, k.name
)
where HatX > 0 and HatY = 0
order by
  kdnr, name
Schade, dass FireBird Minus nicht unterstützt, dann wäre ein einfaches
SQL-Code:
select * from kunden
join rechnungen on rechnungen.kdnr = kunden.kdnr
join positionen on positionen.renr = rechnungen.renr and positionen.artikel = 4701
minus
select * from kunden
join rechnungen on rechnungen.kdnr = kunden.kdnr
join positionen on positionen.renr = rechnungen.renr and positionen.artikel = 5702
möglich.

Delphi.Narium 10. Apr 2024 13:38

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Da es mir keine Ruhe lässt, eine Lösung ohne Subselect zu finden, hab' ich mal den ChatBot meiner Wahl befragt, diese Lösung wurde extrem schnell geliefert:
Zitat:

Zitat von Frage
Ich möchte alle Kunden haben, die den Artikel X gekauft haben und nie den Artikel Y gekauft haben. (Hinweis die Artikel X und Y können in verschiedenen Rechnungen des selben Kunden auftreten). Die Tabellen sehen so aus:

Kunden
------
KdNr, Name, ...

Rechnungen
-----------
KdNr, ReNr, ....

Positionen
----------
ReNr, Artikel, ....

Zitat:

Zitat von Antwort
Sie können die folgende SQL-Abfrage verwenden, um alle Kunden zu finden, die Artikel X gekauft haben und nie Artikel Y gekauft haben. Bitte ersetzen Sie 'X' und 'Y' durch Ihre tatsächlichen Artikelnamen.

SQL-Code:
SELECT k.*
FROM Kunden k
WHERE EXISTS (
    SELECT 1
    FROM Rechnungen r
    JOIN Positionen p ON r.ReNr = p.ReNr
    WHERE r.KdNr = k.KdNr AND p.Artikel = 'X'
)
AND NOT EXISTS (
    SELECT 1
    FROM Rechnungen r
    JOIN Positionen p ON r.ReNr = p.ReNr
    WHERE r.KdNr = k.KdNr AND p.Artikel = 'Y'
)
Zitat:

Zitat von Fortsetzung der Antwort
In dieser Abfrage wird zuerst geprüft, ob ein Kunde einen Artikel X gekauft hat ('EXISTS'-Teil), und dann wird sichergestellt, dass der Kunde nie einen Artikel Y gekauft hat ('NOT EXISTS'-Teil).

Aber, wir möchten es doch ohne Subselect, daher eine Nachfrage:
Zitat:

Zitat von Frage
Gibt es auch eine Lösungsmöglichkeit für diese Fragestellung, die ohne Subselect auskommt?

Zitat:

Zitat von Antwort
Ja, es ist möglich, eine Lösung ohne Subselects zu erstellen, indem Sie 'JOIN' und 'GROUP BY' verwenden. Hier ist ein Beispiel:

SQL-Code:
SELECT k.KdNr, k.Name
FROM Kunden k
JOIN Rechnungen r ON k.KdNr = r.KdNr
JOIN Positionen p ON r.ReNr = p.ReNr
GROUP BY k.KdNr, k.Name
HAVING SUM(CASE WHEN p.Artikel = 4701 THEN 1 ELSE 0 END) > 0
AND SUM(CASE WHEN p.Artikel = 5702 THEN 1 ELSE 0 END) = 0
Zitat:

Zitat von Fortsetzung der Antwort
In dieser Abfrage wird die 'SUM'-Funktion mit einer 'CASE'-Anweisung verwendet, um zu zählen, wie oft jeder Kunde Artikel X und Y gekauft hat. Kunden, die Artikel X mindestens einmal gekauft haben und Artikel Y nie gekauft haben, werden in den Ergebnissen enthalten sein.

Bitte beachten Sie, dass diese Abfrage möglicherweise nicht so effizient ist wie die vorherige mit Subselects, insbesondere wenn Ihre Tabellen sehr groß sind. Es ist immer eine gute Idee, die Leistung zu überwachen und gegebenenfalls Indizes hinzuzufügen.

Es geht also doch ;-)

jaenicke 10. Apr 2024 14:36

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
sum über case ist aber vermutlich die langsamste Variante, weil dies kaum optimiert werden kann. Meine letzte Variante ist auch kein echtes Subselect, weil die zweite Abfrage mit join kombiniert und nicht in jeder Zeile ausgeführt wird.

Holger hat mit seinem Einwand aber natürlich auch Recht:
Zitat:

Zitat von IBExpert (Beitrag 1535547)
ich finde sowas einfacher zu verstehen und modularer, weil auch mit anderen bedingungen einfach zu kombinieren und trotzdem
die unterbedingungen einfach lesbar sind.


Uwe Raabe 10. Apr 2024 15:59

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1535576)
Aber damit ist die Vermutung widerlegt:

Nicht zwingend.
SQL-Code:
SELECT K.KdNr, K.Name
FROM Kunden K
INNER JOIN Rechnungen R ON K.KdNr = R.KdNr
LEFT JOIN Positionen P1 ON R.ReNr = P1.ReNr AND P1.Artikel = 4701
LEFT JOIN Positionen P2 ON R.ReNr = P2.ReNr AND P2.Artikel = 5702
GROUP BY K.KdNr, K.Name
HAVING COUNT(P1.ReNr) > 0 and COUNT(P2.ReNr) = 0;
Allerdings lassen die nur rudimentär vorhandenen Testdaten eigentlich keine belastbare Aussage zu.

Unschön ist halt, dass man die Kunden-Felder zumindest im GROUP BY einzeln aufführen muss (Ich kenne zumindest kein System, das dort ein K.* akzeptiert). Allerdings ist man dann vielleicht eher gewillt, nur die wirklich benötigten Felder anzugeben. Wäre ja denkbar, dass lediglich die KdNr benötigt wird, dann wird es recht simpel.

Delphi.Narium 10. Apr 2024 21:41

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1535596)
Zitat:

Zitat von Delphi.Narium (Beitrag 1535576)
Aber damit ist die Vermutung widerlegt:

Nicht zwingend.

Deshalb schrieb ich ja später auch
Zitat:

Zitat von Delphi.Narium
Es geht also doch ;-)

Zitat:

Zitat von Uwe Raabe (Beitrag 1535596)
SQL-Code:
SELECT K.KdNr, K.Name
FROM Kunden K
INNER JOIN Rechnungen R ON K.KdNr = R.KdNr
LEFT JOIN Positionen P1 ON R.ReNr = P1.ReNr AND P1.Artikel = 4701
LEFT JOIN Positionen P2 ON R.ReNr = P2.ReNr AND P2.Artikel = 5702
GROUP BY K.KdNr, K.Name
HAVING COUNT(P1.ReNr) > 0 and COUNT(P2.ReNr) = 0;

Das ist eine Abwandlung/Weiterentwicklung der von der KI weiter oben gelieferten Antwort oder hat zumindest große Ähnlichkeit mit ihr. Sie hat den Vorteil, dass sie ohne Case und Sum auskommt und sie ist dadurch deutlich eleganter und lesbarer (und vermutlich bei größeren Datenmengen auch schneller).
Zitat:

Zitat von Uwe Raabe (Beitrag 1535596)
Allerdings lassen die nur rudimentär vorhandenen Testdaten eigentlich keine belastbare Aussage zu.

Das war für mich auch das Hauptproblem erstmal Testdaten zu erstellen, die wirklich alle möglichen Fälle abdecken, hoffe aber, dass mir das in https://www.delphipraxis.net/1535576-post20.html gelungen ist.

Interessant ist: Wir haben jetzt acht unterschiedliche und funktionierende Lösungen zur Aufgabenstellung.

Uwe Raabe 11. Apr 2024 09:12

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
Zitat:

Zitat von Delphi.Narium (Beitrag 1535615)
Das ist eine Abwandlung/Weiterentwicklung der von der KI weiter oben gelieferten Antwort oder hat zumindest große Ähnlichkeit mit ihr.

In der Tat. Ich muss den Part deiner Antwort irgendwie überlesen haben, sonst hätte ich mich darauf bezogen. Sorry!

IBExpert 12. Apr 2024 06:09

AW: Abfrage: Kund hat Artikel X aber nicht Y gekauft
 
so als technische basisinfo noch:

ein sum befehl wird immer die komplette ergebnismenge abklappern, spricht, wenn
du tausend kunden hast die alle je 1000 rechnungen mit dem verkauften produkt hatten,
kommt der server nicht drumherum, alle eine Million datensätze zu analysieren, um die komplett
unwichtige Information der summe zu ermitteln (war ja im threadtitel irrelevant).

Ein exists ist zumindest bei firebird so implementiert, das dieser sobald ein record
in der ergebnismenge gefunden wurde (bei der hoffentlich passende indizes nutzbar sind)
sofort die weiteren teilergebnisse ignoriert, also in dem o.a. fall mit passenden indizes
statt 1000000 operationen das ergebnis mit 1000 operationen ermitteln kann.

Bei single user datenbanken wird das kaum keiner merken, wenn aber dutzende oder hunderte user
solche abfragen in schlechter version parallel machen und so ermittelte unwichtige count
oder sum befehle auf einem Startscreen einer Applikation darstellt, dann wird der server
niemals performant sein, weil der immer nur schwachsinn mit mörderaufwand treibt.

wenn dann noch dazu kommt, das die 1000000 rechnungen und die 1000 kunden nicht über passende
indizes verfügen, reden wir auch intern nicht über 1000000 operationen sondern schnell mal über
1000000000 operationen.

firebird liefert sehr gute hilfsmittel um so was für die eigenen SQLs zu erkennen, aber das
Design der eigenen SQLs muss der programmierer verantworten. Wenn man eine summe brauchen würde
geht es nicht anders, aber aus der summe >= eigentlich nur die existenz auszuwerten ist
um es mal freundlich zu sagen unglücklich programmiert.


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:53 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz