argoadmin
Goto Top

Firebird SQL Frage

Hallo.

Wir haben auf einem Windowsserver-System eine Firebird (2.5) Datenbank laufen und in einer Tabelle kundenvor einigen Monaten ein paar tausend neue Kunden aus einer externen Quelle hinzugefügt.
Zu diesen Kunden wurde automatisch ein Kontakt in der Tabelle kontakthinzugefügt. Bei all diesen importierten Kontakten wurde im Feld kontaktarteingefügt:" Kunde importiert".

Dadurch wurden neue Kunden generiert mit jeweils einem Kontaktdatensatz.

Mit:
select kunden.kundennr, kunden.kundname, kontakte.kontaktart
from kunden, kontakte
where kunden.kundennr = kontakte.kundennr
and kontakte.kontaktart = 'Kunde importiert'

bekomme ich all diese 'Datensätze angezeigt.

Nun sind in der Zwischenzeit bei einigen von diesen Kunden Kontakte hinzugekommen, bei vielen anderen nicht.
D. h. manche Kunden haben entweder mehrere Kontaktdatensätze, die anderen lediglich diesen einen.

Mein Chef möchte nun diejenigen Kunden, die keinen weiteren Kontaktsatz hinzubekommen haben, aus der Datenbank entfernen bzw. auf inaktiv setzen.
Somit sollten nur die übrig bleiben, die mehr als 1 Kontakt haben.

Und genau hier ist mein Problem - wie bekomme ich nur diese Kunden (mit nur einem Kontakt) zu fassen, um die Sätze zu manipulieren.

Ist mein Problem verständlich bzw. braucht ihr noch irgendwelche Angaben?
Und verzeiht mir, falls meine Frage hier völlig falsch ist.

Gruß aus Hamburg

Content-Key: 336463

Url: https://administrator.de/contentid/336463

Ausgedruckt am: 19.03.2024 um 08:03 Uhr

Mitglied: Apophis
Apophis 28.04.2017 aktualisiert um 16:49:54 Uhr
Goto Top
Wenn Dir Stichworte reichen: Gruppieren mit GROUP BY und die HAVING-Klausel sind Dein Freund.

Etwa:

SELECT Kunden.Kundennummer
FROM Kunden
GROUP BY Kunden.Kundennummer
HAVING Count(*)=1

Dies würde alle Kundennummern ausgeben, die exakt einmal vorhanden sind.

Erweiterungen um weitere Daten überlasse ich Dir, es ist Freitagabend face-smile

Gruß
Apophis
Mitglied: Biber
Biber 29.04.2017 um 13:12:58 Uhr
Goto Top
Moin Argoadmin,

versuch es so:
SELECT kunden.kundennr, kunden.kundname, kontakte.kontaktart
FROM kunden, kontakte
WHERE kunden.kundennr in (

    SELECT kontakte.kundennr 
    FROM kontakte 
    GROUP BY kontakte.kundennr
    HAVING Count(*) = 1
 ) 
  AND kontakte.kontaktart = 'Kunde importiert';  

Im Subselect werden alle Kontakte.kundennr mit nur einer Kontaktart ermittelt.
Im äußeren SELECT wird zusätzlich noch geprüft, ob die Kontaktart auch 'Kunde importiert' ist.

Grüße
Biber
Mitglied: Argoadmin
Argoadmin 02.05.2017 um 10:30:25 Uhr
Goto Top
Hi Apophis,

das werde ich heute Nachmittag mal testen. Hört sich aber schon mal gut an.
Ich melde mich danach noch mal.

Danke face-smile
Mitglied: Argoadmin
Argoadmin 02.05.2017 um 10:39:15 Uhr
Goto Top
Hi Biber,

der zweite mit der having-Klausel, dann scheint da was dran zu sein.
Ich teste nachher auch die Verison mit dem Subselect.
Vielen Dank face-smile
Mitglied: Argoadmin
Argoadmin 02.05.2017 um 14:10:11 Uhr
Goto Top
Hi Biber,

ich habe folgendes statement abgeschickt:

SELECT kunden.kundennr, kontakte.kontaktart
FROM kunden, kontakte
WHERE kunden.kundennr in
(
SELECT kontakte.kundennr
FROM kontakte
GROUP BY kontakte.kundennr
HAVING count(*) = 1
)
AND kontakte.kontaktart = 'Kunde importiert'

Das Ergebnis ist dann eine Liste mit den richtigen Kundennummern (insoweit scheint alles korrekt zu laufen), aber dann wird der Bildschirm "blass" und die Routine hört nicht auf zu arbeiten. Ich habe es dann nach 30 Minuten abgebrochen.

Die Datenmenge ist nicht grad gigantisch - es handelt sich um ca. 10.000 Datensätze, aus denen selektiert werden soll, Server (VM ESCXi 5.5) hat 96 GB RAM, sollte reichen.

Wäre toll, wenn Dir was dazu einfällt.

Danke und Gruß aus Hamburg
Mitglied: Biber
Lösung Biber 02.05.2017 aktualisiert um 18:09:07 Uhr
Goto Top
Moin Argoadmin,

ich habe hier kein Firebird zur Hand, kann also nur gedanklich mittesten.

Aber daran kann ich dich gerne teilhaben lassen.

Mein nächster Gedanke wäre/war der Versuch, ganz auf einen wie-auch-immer gearteten JOIN zu verzichten.
Denn der mag funktionieren für einen SELECT, aber du willst ja irgendwann minimal ein UPDATE, wahrscheinlich sogar ein DELETE auf beide beteiligten Tabellen (Kontakte und Kunden) abfeuern. Natürlich nacheinander.

Ach ja, da es natürlich auch sein KÖNNTE, dass irgendwelche "kunden"-Datensätze existieren, die keinen einzigen "kontakte"-Childsatz haben, deshalb hole ich mir immer erst die KUNDENNRn aus "kontakte". Denn du willst ja genau diese 'Kunde importiert'-Dinger haben, und die SIND in "kontakte".

Da würde dann so etwas rauskommen.
SELECT k.*
FROM kunden  k
WHERE k.kundennr in
(
SELECT kontakte.kundennr
FROM kontakte
GROUP BY kontakte.kundennr
HAVING count(*) = 1 AND max(kontakte.kontaktart) = 'Kunde importiert'  
)
;
-> Hier für alle KUNDEN-Datensätze; ebenso kannst du der zweiten Zeile natürlich auch das Wort "kunden" durch "kontakte" ersetzen beim SELECT.


Wenn deine FireBird-Version auch CTEs unterstützt - soll bei neueren Versionen so sein -, dann ggf. auch umformuliert zu

With only1ImportKontakt (kundennnr) as
(
SELECT kundennr
  FROM kontakte
  GROUP BY kundennr
  HAVING count(*) = 1 and max(kontaktart) = 'Kunde importiert'  
)

SELECT K.* from Kunden  k
where Kunden.kundennr In (select kundennr from only1Importkontakt);
-- oder als where exists formuliert
-- where exists (select 1 from only1Importkontakt o where o.kundennr=k.kundennr) 

-oder-, noch lesbarer, und weil ich nicht weiss, ob die HAVING()-Clause mit "AND max(..)" so von FireBird gefressen wird
With only1Kontakt (kundennnr, kontaktart) as
(
SELECT kundennr, max(kontaktart)
  FROM kontakte
  GROUP BY kundennr
  HAVING count(*) = 1 
 )
, only1Importkontakt(kundennr) as
( select kundennr from only1Kontakt o
  WHERE o.kontaktart  = 'Kunde importiert'  
)

SELECT Kunden.* from Kunden  k
 where K.kundennr In (select kundennr from only1Importkontakt)
-- oder:
-- where exists (select 1 from only1Importkontakt o where o.kundennr=k.kundennr) 
;
-- auch hier wieder liesse sich das Wort "kunden" in der untersten SELECT-Abrage wieder durch "kontakte" ersetzen, um erst mal zu sehen, um welche Datensätze es geht.

Hilft aber alles nix - für UPDATE oder DELETE scheinen CTEs bei FireBird nicht ausgelegt zu sein.
Bzw. nur mit einem MERGE/ USING CTE ... ON..-Befehl (laut internet), aber den bastel ich nicht hypothetisch zusammen - scheint etwas holziger zu sein.

Also wäre am Ende der Gedankenspiele mein pragmatischer Ansatz:
  • leg dir mit "CREATE Table Eintagskunden " eine echte Tabelle an mit einem Feld "Kundennr" und dem passenden Datentyp und PK "kundennr"
  • Jage mit ....
INSERT INTO Eintagskunden(kundennummer)  
SELECT kundennr
  FROM kontakte
  GROUP BY kundennr
  HAVING count(*) = 1 and max(kontaktart) = 'Kunde importiert'  
;
... alle Kundennrn, die a) nur einen Kontakt haben, welcher b) von Typ 'kunde importiert' ist rein in diese Tabelle.


Dann kannst du ohne Verrenkungen sowohl beim SELECT wie auch bei DELETEs oder UPDATES auf alle relevanten KUNDENNRn Bezug nehmen.

Und zwar gleichermaßen von "kunden" wie von "kontakte" aus.
So wie oben, mit "WHERE ... in (SELECT ..FROM Eintagskunden)" oder "WHERE EXISTs", ebenfalls s.o.

Denn, da du wahrscheinlich ja referentielle Integrität hinterlegt hast, musst du ja zuerst die Child-Datensätze in "Kontakte" löschen, bevor du die Parent-Datensätze in "kunden" löschen darfst.
Es sei denn, bei FireBird gibt es ein DELETE CASCADE... aber da frag ich jetzt keine Suchmaschine.
Evtl. ist ja jemand im Forum, der FireBird griffbereit und ein wenig Spass am Antesten hat.

Versuch mal, wie weit sich meine trockene Theorie in lebendige Praxis umsetzen lässt. (aber nich' schimpfen...) face-wink

Grüße
Biber
Mitglied: Argoadmin
Argoadmin 04.05.2017 um 16:06:14 Uhr
Goto Top
Hallo Biber,

konnte leider erst jetzt antworten, einige Chefs meinen immer noch, 12 Stunden arbeiten am Tag seien viel zu wenig.

Meine Herren - da hat aber jemand mächtig Ahnung! Top!
Habe nicht schlecht gestaunt über das, was Du mir alles angeboten hast.
Und ja, ich wollte natürlich ein Update rüberjagen, was mit Deiner ersten Lösung auch bestens geklappt hat.

Ich danke Dir vielmals face-smile

Schönen Gruß aus Hamburg