AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken Abfrage: Kund hat Artikel X aber nicht Y gekauft
Thema durchsuchen
Ansicht
Themen-Optionen

Abfrage: Kund hat Artikel X aber nicht Y gekauft

Ein Thema von BlueStarHH · begonnen am 8. Apr 2024 · letzter Beitrag vom 12. Apr 2024
Antwort Antwort
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.785 Beiträge
 
Delphi 12 Athens
 
#1

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

  Alt 10. Apr 2024, 10:14
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?
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke
Online

Registriert seit: 10. Jun 2003
Ort: Berlin
10.078 Beiträge
 
Delphi 12 Athens
 
#2

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

  Alt 10. Apr 2024, 11:12
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.
Sebastian Jänicke
AppCentral

Geändert von jaenicke (10. Apr 2024 um 11:16 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.785 Beiträge
 
Delphi 12 Athens
 
#3

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

  Alt 10. Apr 2024, 11:34
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?
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke
Online

Registriert seit: 10. Jun 2003
Ort: Berlin
10.078 Beiträge
 
Delphi 12 Athens
 
#4

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

  Alt 10. Apr 2024, 11:53
Nein, denn wie du sagst, die Kunden mit B fliegen in der Zeile ja schon raus.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.785 Beiträge
 
Delphi 12 Athens
 
#5

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

  Alt 10. Apr 2024, 12:01
Hmmm, ich habe mal dein Beispiel entsprechend ergänzt:
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.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming

Geändert von Uwe Raabe (10. Apr 2024 um 12:39 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke
Online

Registriert seit: 10. Jun 2003
Ort: Berlin
10.078 Beiträge
 
Delphi 12 Athens
 
#6

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

  Alt 10. Apr 2024, 12:25
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;
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.600 Beiträge
 
Delphi 7 Professional
 
#7

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

  Alt 10. Apr 2024, 13:05
Aber damit ist die Vermutung widerlegt:
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.
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.600 Beiträge
 
Delphi 7 Professional
 
#8

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

  Alt 10. Apr 2024, 13:38
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 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 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 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 von Frage:
Gibt es auch eine Lösungsmöglichkeit für diese Fragestellung, die ohne Subselect auskommt?
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 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
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.785 Beiträge
 
Delphi 12 Athens
 
#9

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

  Alt 10. Apr 2024, 15:59
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.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:01 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz