Zugriff auf eine n:m Verknüpfung

Für Themen zu Datenbanken und Zugriff auf diese. Auch für Datenbankkomponenten.
Luckner
Beiträge: 88
Registriert: Sa 18. Jan 2020, 09:56
OS, Lazarus, FPC: Winux (L 2.2.0 FPC 3.2.2)
CPU-Target: Windows 64-Bit

Zugriff auf eine n:m Verknüpfung

Beitrag von Luckner »

Hallo,
habe, möglicherweise, eine triviale Frage. in einer Datenbank habe ich ein 1. Kunden-Tabelle und 2. Lieferadressen-Tabelle. Die Kunden haben verschiedene Lieferadressen und es kommt vor, dass eine Lieferadresse mehreren Kunden zugerordnet werden muss. Habe deswegen eine 3. Tabelle mit ID, IDKUNDE, IDLIEFERADRESSEN erstellt. Wie macht man eine select-Abfrage, wenn man für einen Kunden die Lieferadressen filtern möchte. Mein Gedanke ist, dass über eine select-Anweisung mit where IDKUNDE = Zahl und in einer while-Schleife dann ein weiteres select mit where = entsprechende IDLIEFERADRESSEN. Aber ich kann mir jedoch vorstellen, dass es dafür eine schnellere Anweisung gibt. Möglicherweise irgendetwas mit JOIN.

Danke, Luckner

Ich934
Lazarusforum e. V.
Beiträge: 316
Registriert: So 5. Mai 2019, 16:52
OS, Lazarus, FPC: ArchLinux und Windows mit FPCUPdeluxe (L: 2.0.X, FPC 3.2.0)
CPU-Target: x86_64, i386
Wohnort: Bayreuth

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von Ich934 »

Hi,

schon mal was von JOINS im SELECT gehört?

Code: Alles auswählen

SELECT *
FROM kunden
LEFT JOIN verbindungstabelle ON idkunde = verbindungskundenid
LEFT JOIN lieferadresse ON verbindungslieferadressenid = lieferadressenid
Schneller sollte es nicht gehen...

cu tb
Tipp für PostgreSQL: www.pg-forum.de

Benutzeravatar
six1
Beiträge: 782
Registriert: Do 1. Jul 2010, 19:01

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von six1 »

Kunde hat eine lfdnr
Lieferadressen haben eine lfdnr
Tabelle Kunde_Lieferadresse hat die Einträge: Kunde.lfdnr und Lieferadresse.lfdnr

Code: Alles auswählen

Query1.sql.text:=
'Select Lieferadresse.* from Kunde '+
' left join Kunde on Kunde.lfdnr=:KUNDE '+
' left join Kunde_Lieferadresse on Kunde_Lieferadresse.kunde=Kunde.lfdnr '+
' left join Lieferadresse on Lieferadresse.lfdnr=Kunde_Lieferadresse.Lieferadresse '+
'where Kunde.lfdnr=:KUNDE;';

Query1.Parambyname('KUNDE').asinteger:=KundeID;
...ohne Prüfung hingeschrieben, teste mal


Edit: Ich934 war schneller...
Gruß, Michael

charlytango
Beiträge: 843
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz stable (2.2.6, 3.x)
CPU-Target: Win 32/64, Linux64
Wohnort: Wien

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von charlytango »

Luckner hat geschrieben:
Di 29. Mär 2022, 10:20
where IDKUNDE = Zahl
Wenn er die ID des Kunden schon weiß dann braucht es in dem SELECT keine Kundentabelle.
Wird in einer großen DB sicher schneller, auch wenn der DB-Optimizer sicher zuerst die Daten aus der Kundentabelle holt und die hoffentlich auf ID indiziert ist.

Und wenn das eine normale Abfrage der Lieferadressen eines Kunden sein soll, dann brauchts auch kein LEFT JOIN, denn wenn es keine Lieferadressen gibt, ist die Abfrage eben leer. LEFT JOIN würde dazu führen dass auch Kunden ohne Lieferadresse ausgegeben würden. Wenn es nur um die Lieferadressen geht reicht ein JOIN
Das reicht auch zur Anzeige in einem entsprechenden Grid oder einer Auswahlmöglichkeit.

Ich nehme jetzt einfach an dass SELECT * FROM nur exemplarisch gemeint war, denn den Hauptteil der Performance lässt man beim übertragen unnötiger Tabellenspalten liegen. Besonders im Zeitalter von HomeOffice ist die Datenmenge über langsame Verbindungen ein Thema.

Luckner
Beiträge: 88
Registriert: Sa 18. Jan 2020, 09:56
OS, Lazarus, FPC: Winux (L 2.2.0 FPC 3.2.2)
CPU-Target: Windows 64-Bit

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von Luckner »

Vielen Dank für die Hilfe,
habe jetzt den Vorschlag von Ich934, entsprechend angepasst, verwendet:

Code: Alles auswählen

('SELECT * FROM KUNDENSTAMM ');
('JOIN KUNDENLIEFERADRESSEN ON ' + IntToStr(KundenID) + ' = KUNDENLIEFERADRESSEN.IDKUNDEN');
('JOIN ADRESSENSTAMM ON KUNDENLIEFERADRESSEN.IDADRESSEN = ADRESSENSTAMM.ID');
Es werden schon die richtigen Lieferadressen angezeigt, jedoch multipliziert mit der Anzahl der Kunden in der Kundentabelle. Also in der Kundentabelle gibt es 4 Kunden. Der Kunde "Meier = ID=1" hat 3 Einträge in der Verbindungstabelle 1:1, 1:3, 1:4, dann wird in der entprechendem Grid (verknüpft mit entsprechendem IBDatasource) 4x die Lieferadressen angezeigt. Den Rowcount habe ich auch ausgegeben und es kommt eine 11 raus. Also 12 Einträge. Auch bei wegnahme von 'LEFT' keine Änderung.

Gruß, Luckner

Luckner
Beiträge: 88
Registriert: Sa 18. Jan 2020, 09:56
OS, Lazarus, FPC: Winux (L 2.2.0 FPC 3.2.2)
CPU-Target: Windows 64-Bit

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von Luckner »

Nicht den Rowcount, sondern den Recordcount.

Luckner

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von af0815 »

Die Frage ist immer, welche Informationen du anzeigen willst :shock:

Hier werden mal die Auswirkungen der verschiedenen Joins gezeigt
https://www.ionos.at/digitalguide/hosti ... uter-join/

Recordcount würde ich nicht verwenden, außer du kennst genau die Einschränkungen und Auswirkungen. Genaugenommen ist das ein FetchCount.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Luckner
Beiträge: 88
Registriert: Sa 18. Jan 2020, 09:56
OS, Lazarus, FPC: Winux (L 2.2.0 FPC 3.2.2)
CPU-Target: Windows 64-Bit

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von Luckner »

Hallo af0815,

ich würde gerne in einem Grid die Lieferadressen eines Kunden anzeigen. Wobei versch. Kunden auch die gleichen Lieferadressen haben können.

Gruß, Luckner

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von af0815 »

Meintest du sowas ? Oder habe ich da was falsch verstanden.

Code: Alles auswählen

SELECT        dbo.X_Kunden.Name, dbo.X_Adr.Adresse
FROM            dbo.X_MN_KundenAdr INNER JOIN
                         dbo.X_Adr ON dbo.X_MN_KundenAdr.AdrID = dbo.X_Adr.AdrID RIGHT OUTER JOIN
                         dbo.X_Kunden ON dbo.X_MN_KundenAdr.KundenID = dbo.X_Kunden.NameID
Ergebnis:
Name Adresse
Kunde1 Adresse 1
Kunde1 Adresse 2
Kunde2 Adresse 1
Kunde3 Adresse 3
Das ist meine schnelle DemoDB

Code: Alles auswählen

USE [Adressbuch]
GO
/****** Object:  Table [dbo].[X_Kunden]    Script Date: 31.03.2022 10:55:01 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[X_Kunden](
	[NameID] [int] NOT NULL,
	[Name] [nvarchar](50) NOT NULL
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[X_Adr]    Script Date: 31.03.2022 10:55:01 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[X_Adr](
	[AdrID] [int] NOT NULL,
	[Adresse] [nvarchar](50) NOT NULL
) ON [PRIMARY]
GO
/****** Object:  Table [dbo].[X_MN_KundenAdr]    Script Date: 31.03.2022 10:55:01 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[X_MN_KundenAdr](
	[MNID] [int] NOT NULL,
	[KundenID] [int] NOT NULL,
	[AdrID] [int] NOT NULL
) ON [PRIMARY]
GO
/****** Object:  View [dbo].[XV_KundenAdr]    Script Date: 31.03.2022 10:55:01 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE VIEW [dbo].[XV_KundenAdr]
AS
SELECT        dbo.X_Kunden.Name, dbo.X_Adr.Adresse
FROM            dbo.X_MN_KundenAdr INNER JOIN
                         dbo.X_Adr ON dbo.X_MN_KundenAdr.AdrID = dbo.X_Adr.AdrID RIGHT OUTER JOIN
                         dbo.X_Kunden ON dbo.X_MN_KundenAdr.KundenID = dbo.X_Kunden.NameID
GO
INSERT [dbo].[X_Adr] ([AdrID], [Adresse]) VALUES (1, N'Adresse 1')
GO
INSERT [dbo].[X_Adr] ([AdrID], [Adresse]) VALUES (2, N'Adresse 2')
GO
INSERT [dbo].[X_Adr] ([AdrID], [Adresse]) VALUES (3, N'Adresse 3')
GO
INSERT [dbo].[X_Kunden] ([NameID], [Name]) VALUES (1, N'Kunde1')
GO
INSERT [dbo].[X_Kunden] ([NameID], [Name]) VALUES (2, N'Kunde2')
GO
INSERT [dbo].[X_Kunden] ([NameID], [Name]) VALUES (3, N'Kunde3')
GO
INSERT [dbo].[X_MN_KundenAdr] ([MNID], [KundenID], [AdrID]) VALUES (1, 1, 1)
GO
INSERT [dbo].[X_MN_KundenAdr] ([MNID], [KundenID], [AdrID]) VALUES (2, 2, 1)
GO
INSERT [dbo].[X_MN_KundenAdr] ([MNID], [KundenID], [AdrID]) VALUES (3, 3, 3)
GO
INSERT [dbo].[X_MN_KundenAdr] ([MNID], [KundenID], [AdrID]) VALUES (4, 1, 2)
GO
DB: MS-SQL free unter Ubuntu laufend, Verwaltet mit den normalen Microsoft SQL Server Managment Studio (SSMS) unter Windows 10/64
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Luckner
Beiträge: 88
Registriert: Sa 18. Jan 2020, 09:56
OS, Lazarus, FPC: Winux (L 2.2.0 FPC 3.2.2)
CPU-Target: Windows 64-Bit

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von Luckner »

Nein. Ich meine:

Kunde1 Adresse1
Adresse2
Adresse7
Adresse28

Luckner
Beiträge: 88
Registriert: Sa 18. Jan 2020, 09:56
OS, Lazarus, FPC: Winux (L 2.2.0 FPC 3.2.2)
CPU-Target: Windows 64-Bit

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von Luckner »

Die Adressen natürlich untereinander

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von af0815 »

Die Ausblendung von mehrfachen gleichen Kundennamen geht nicht, da im Recordset hier immer Kunde + Adresse vorhanden ist. Die unterdrückung der doppelten Namen, muss du anders lösen oder Query mit Subquery lösen. In einem Recordset sehe ich da keine einfache Möglichkeit.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Luckner
Beiträge: 88
Registriert: Sa 18. Jan 2020, 09:56
OS, Lazarus, FPC: Winux (L 2.2.0 FPC 3.2.2)
CPU-Target: Windows 64-Bit

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von Luckner »

Ich weiß nicht, ob ich das Problem richtig erklärt hatte. Es werden mit dem o.g. select-Aufruf die richtigen Lieferadressen für diesen Kunden angezeigt. Jedoch multipliziert mit der Anzahl der Kunden in der Kundentabelle. Also so: für Kunden Müller:
Berlin
Bremen
München
Köln
Berlin
Bremen
München
Köln
Berlin
Bremen
München
Köln

bei 3 Kunden in der Kundentabelle. Das Programm soll nur die Adressen des einen Kunden zeigen.
Was funktioniert, wenn ich aus der Verbindungstabelle über eine select-Anweisung mit einer Kundenentsprechenden_ID alle dazugehörigen Lieferadressen_ID's herausfiltere und dann mit einer While-Schleife und einer Select-Anweisung mit der aktl. Lieferadressen_ID die Daten aus der Lieferadressen-Tabelle heraussuche.

Luckner.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von af0815 »

Luckner hat geschrieben:
Do 31. Mär 2022, 13:10
Berlin
Bremen
München
Köln
Berlin
Bremen
München
Köln
Berlin
Bremen
München
Köln
Wenn das passiert so hast du deine JOINS falsch gesetzt.

Wenn ich bei meinem SELECTStatement nur eine where Klausel hinzufüge, so kann ich das auf einen Kunden einschränken

Code: Alles auswählen

SELECT        dbo.X_Kunden.Name, dbo.X_Adr.Adresse
FROM            dbo.X_MN_KundenAdr INNER JOIN
                         dbo.X_Adr ON dbo.X_MN_KundenAdr.AdrID = dbo.X_Adr.AdrID RIGHT OUTER JOIN
                         dbo.X_Kunden ON dbo.X_MN_KundenAdr.KundenID = dbo.X_Kunden.NameID
WHERE dbo.X_Kunden.Name = :SQLKunde
Ergebnis
Name Adresse
Kunde1 Adresse 1
Kunde1 Adresse 2
Mach das mit den Partametern im JOIN, ausser du weisst wirklich was du machst. Generell gehören so Einschränkungen immer in die where Klausel.

Ausserdem sollte man Parameter verwenden, hier habe ich den Parameter SQLKunde verwendet.
Verwendet man im Quelltext so

Code: Alles auswählen

    Q.Params.CreateParam(ftString, 'SQLKunde ', ptInput);
    Q.ParamByName('SQLKunde ').AsString:= KundenNamen;
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Sieben
Beiträge: 202
Registriert: Mo 24. Aug 2020, 14:16
OS, Lazarus, FPC: Ubuntu Xenial 32, Lazarus 2.2.0, FPC 3.2.2
CPU-Target: i386

Re: Zugriff auf eine n:m Verknüpfung

Beitrag von Sieben »

Wird nicht der Parameter bereits dadurch erzeugt, dass man einen SQL-Text mit einem Bestandteil ':Ausdruck' zuweist? Ich habe jedenfalls noch nie ein Param 'händisch' anlegen müssen, auch nicht bei Zuweisung erst zur Laufzeit.

Antworten