Announcement

Collapse
No announcement yet.

Verknüpfung eines Fremdschlüssels mit einem zusammengesetzten Primärschlüssel (ACCESS)?

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Verknüpfung eines Fremdschlüssels mit einem zusammengesetzten Primärschlüssel (ACCESS)?

    Hallo zusammen,

    ich möchte zwei Tabellen in einer Access 2000 DB miteinander verknüpfen. Solange es sich dabei um eine einfache Master-Detail-Verbindung handelt, ist dies auch nicht weiter problematisch, d.h. es existiert zu einem Fremdschlüssel ein "echter" Primärschlüssel in der anderen Tabelle. Problematisch wird es bei zusammengesetzten Primärschlüsseln. Ich wollte das Problem mit Hilfe des MSDN lösen. Microsoft schlägt folgenden Lösungsansatz in Visual Basic vor:

    "Creating One-to-Many Relationships (Foreign Keys)
    One-to-many relationships between tables (where the primary key value in the primary table may appear in <B>multiple rows</B> [!] in the foreign table) are established by creating foreign keys. A foreign key is a column or combination of columns whose values match the primary key of another table. Unlike a primary key, a foreign key does not have to be unique."

    ADOX
    <PRE>
    Sub ADOCreateForeignKey()

    Dim cat As New ADOX.Catalog
    Dim tbl As ADOX.Table
    Dim fk As New ADOX.Key

    ' Open the catalog
    cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
    "Data Source=.\NorthWind.mdb;"

    ' Get the table for the foreign side of the relationship
    Set tbl = cat.Tables("Products")

    ' Create the Foreign Key
    fk.Name = "CategoriesProducts"
    fk.Type = adKeyForeign
    fk.RelatedTable = "Categories"

    ' Append column(s) in the foreign table to it
    fk.Columns.Append "CategoryId"

    ' Set RelatedColumn property to the name of the corresponding
    ' column in the primary table
    fk.Columns("CategoryId").RelatedColumn = "CategoryId"

    ' Append the Key object to the Keys collection of Table
    tbl.Keys.Append fk

    Set cat = Nothing

    End Sub
    </PRE>

    Wie kann ich die Zeile "tbl.Keys.Append fk" in Delphi 5 umsetzen? Wie müssen die restlichen Parameter des Delphi-Äquivalents "_Keys.Append()" gefüllt werden? Der folgende Code produziert nur Laufzeit-Fehlermeldungen:

    <PRE>
    (...)
    var
    ADOXKey: _Key;

    ADOXKey := CoKey.Create;
    try
    ADOXKey.Name := 'Tabelle1Tabelle2';
    ADOXKey.Type_ := adKeyForeign;
    ADOXKey.RelatedTable := 'Tabelle1';

    ADOXColumn := ADOXTable.Columns.Item['ID1'] as _Column;
    ADOXKey.Columns.Append(ADOXColumn.Name, ADOXColumn.Type_, ADOXColumn.DefinedSize);
    ADOXKey.Columns['ID1'].RelatedColumn := 'ID1';

    //weitere Spalten sollen hier folgen

    ADOXTable.Keys.Append(ADOXKey, adKeyForeign, EmptyParam, '', ''); //???
    finally
    ADOXKey := nil;
    end;
    </PRE>

    Was ist an dieser Umsetzung des Microsoft-Beispiels falsch? Bin für jeden Hinweis dankbar!
    Gruss,

    Andreas Kohl

  • #2
    Hallo,

    das folgende Beispielprojekt für das Anlegen einer neuen MDB-Datenbank über die <b>ADOX</b>-Objekte stammt aus meinem neuen Buch <i>ADO und Delphi</i>:
    <pre>
    { ************************************************** **************
    Typ : Hauptformular-Unit
    Autor : Andreas Kosch
    Erstellt am : 21.10.2000
    Compiler : Delphi 5.01 UpdatePack #1
    Betriebssystem : Windows 2000
    Beschreibung : Demonstriert das Erzeugen einer neuen
    ACCESS-Datenbank und einer neuen Tabelle
    über die ADOX-Objekte. Dabei wird in diesem
    Beispiel ein zusammengesetzter Primärschüssel
    angelegt.
    Revisionen : 10.12.2001 Test mit Delphi 6.01 unter Windows XP
    ************************************************** ************** }

    unit NewmdbMultiPKFrm;

    interface

    uses
    Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
    Dialogs, StdCtrls, ComObj, ComCtrls;

    type
    TFormMain = class(TForm)
    MemoLog: TMemo;
    StatusBar1: TStatusBar;
    EditMDB: TEdit;
    Label1: TLabel;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    private
    { Private declarations }
    public
    { Public declarations }
    end;

    var
    FormMain: TFormMain;

    implementation

    {$R *.DFM}

    uses ADOX_TLB {$IFDEF VER140}, Variants {$ENDIF};

    resourcestring
    cCONNECTSTRING = 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=';

    procedure TFormMain.Button1Click(Sender: TObject);
    var
    aCatalog : _Catalog;
    aTable : _Table;
    aColumn : _Column;
    aIndex : _Index;
    sDBPath : String;
    sDS : String;
    vColName : OleVariant;
    begin
    MemoLog.Lines.Clear;
    sDBPath := EditMDB.Text;
    if FileExists(sDBPath) then
    begin
    DeleteFile(sDBPath);
    MemoLog.Lines.Add(Format('Datenbankdatei %s gelöscht',[sDBPath]));
    end;
    aCatalog := CoCatalog.Create;
    MemoLog.Lines.Add('Catalog...');
    sDS := aCatalog.Create(Format('%s%s',[cCONNECTSTRING, sDBPath]));
    MemoLog.Lines.Add(sDS);
    aTable := CoTable.Create;
    MemoLog.Lines.Add('Table...');
    aTable.ParentCatalog := aCatalog;
    aTable.Name := 'Kunden';

    // Spalte 1
    aColumn := CoColumn.Create;
    with aColumn do
    begin
    ParentCatalog := aCatalog;
    Name := 'KdnName';
    Type_ := adVarWChar;
    DefinedSize := 20;
    Properties['Description'].Value := 'Name des Kunden';
    Properties['Nullable'].Value := False;
    end;
    aTable.Columns.Append(aColumn, 0, 0);
    MemoLog.Lines.Add('Spalte 1 hinzugefügt...');

    // Spalte 2
    aColumn := CoColumn.Create;
    with aColumn do
    begin
    ParentCatalog := aCatalog;
    Name := 'Vorname';
    Type_ := adVarWChar;
    DefinedSize := 15;
    Properties['Description'].Value := 'Vorname des Kunden';
    Properties['Nullable'].Value := False;
    end;
    aTable.Columns.Append(aColumn, 0, 0);
    MemoLog.Lines.Add('Spalte 2 hinzugefügt...');

    // Spalte 3
    aColumn := CoColumn.Create;
    with aColumn do
    begin
    ParentCatalog := aCatalog;
    Name := 'eMail';
    Type_ := adVarWChar;
    DefinedSize := 20;
    Properties['Description'].Value := 'eMail-Adresse des Kunden';
    Properties['Default'].Value := '(unbekannt)';
    Properties['Jet OLEDB:Allow Zero Length'].Value := True;
    end;
    aTable.Columns.Append(aColumn, 0, 0);
    MemoLog.Lines.Add('Spalte 3 hinzugefügt...');

    // Spalte 4
    aColumn := CoColumn.Create;
    with aColumn do
    begin
    ParentCatalog := aCatalog;
    Name := 'Telefon';
    Type_ := adVarWChar;
    DefinedSize := 15;
    Properties['Description'].Value := 'Telefonnummer des Kunden';
    Properties['Nullable'].Value := True;
    end;
    aTable.Columns.Append(aColumn, 0, 0);
    MemoLog.Lines.Add('Spalte 2 hinzugefügt...');

    // Tabelle hinzufügen
    aCatalog.Tables.Append(aTable);
    MemoLog.Lines.Add('Tabelle hinzugefügt... ');

    // Primärschlüssel hinzufügen
    aIndex := CoIndex.Create;
    aIndex.Name := 'PKMix';
    vColName := 'KdnName';
    aColumn := aTable.Columns.Item[vColName] as _Column;
    aIndex.Columns.Append(aColumn.Name, aColumn.Type_, aColumn.DefinedSize);
    vColName := 'Vorname';
    aColumn := aTable.Columns.Item[vColName] as _Column;
    aIndex.Columns.Append(aColumn.Name, aColumn.Type_, aColumn.DefinedSize);
    aIndex.PrimaryKey := True;
    aIndex.Unique := True;
    aTable.Indexes.Append(aIndex, EmptyParam);
    MemoLog.Lines.Add('Index hinzugefügt... Fertig!');
    end;

    end.

    </pre>
    In dem Zweiten (von Drei) Beispielprojekt für ADO demonstriere ich den Einsatz von <b>Keys.Append</b>:
    <pre>
    // Spalte 1 = Primärschlüssel
    vColName := 'KdnName';
    aTable.Columns.Append(vColName, adVarWChar, 30);
    aColumn := aTable.Columns.Item[vColName] as _ColumnDisp;
    aColumn.Properties['Description'].Value := 'Name des Kunden';
    MemoLog.Lines.Add('Spalte 1 hinzugefügt...');

    // Primärschlüssel hinzufügen
    aTable.Keys.Append('KdnKey', adKeyPrimary, vColName, '', '');
    MemoLog.Lines.Add('Spalte 1 als Primärschlüssel definiert...');
    </pre&gt

    Comment


    • #3
      Hallo Herr Kosch, vielen Dank für Ihren Tipp!<BR>
      Die zusammengesetzten Primärschlüssel funktionieren so weit prima.<BR>
      Das Problem liegt nun darin, zwei ACCESS Tabellen miteinander zu verknüpfen.<BR>
      Wie kann man in einer ACCESS DB mittels <B>"aTable.Keys.Append()"</B> einen Referenzschlüssel vom Typ <B>"adKeyForeign"</B> auf einen zusammengesetzten Primärschlüssel legen?<BR>Bei nicht-zusammengesetzten Primärschlüsseln ist das unproblematisch.<BR>
      An der Umsetzung für zusammengesetzte Primärschlüssel (Tabelle 1 --> Tabelle 2) bin ich leider bislang gescheitert:
      <P>
      Annahme für den nachfolgenden Quellcode-Auszug:<BR>
      1. Tabelle 1 enthält x Spalten, Spalten 1 bis 3 bilden den zusammengesetzten Primärschlüssel<BR>
      2. Tabelle 2 enthält y Spalten, Spalten 1 bis 3 sollen über Referenzschlüssel mit Tabelle 1 verknüpft werden<BR>
      Tabelle 1, Spalte 1 --> Tabelle 2, Spalte 1<BR>
      Tabelle 1, Spalte 2 --> Tabelle 2, Spalte 2<BR>
      Tabelle 1, Spalte 3 --> Tabelle 2, Spalte 3<BR>
      <BR>
      <PRE>---
      var
      ADOXCatalog: _Catalog;
      ADOXTable: _Table;
      ADOXColumn: _Column;
      ADOXIndex: _Index;
      ADOXKey: _Key;

      (...)

      ADOXKey := CoKey.Create;
      try
      ADOXKey.Name := 'Name';
      ADOXKey.Type_ := adKeyForeign;
      ADOXKey.RelatedTable := 'Tabelle2';

      ADOXColumn := ADOXTable.Columns.Item['Spalte1'] as _Column;
      ADOXKey.Columns.Append(ADOXColumn.Name, ADOXColumn.Type_, ADOXColumn.DefinedSize);
      ADOXKey.Columns['Spalte1'].RelatedColumn := 'Spalte1'; //die Spaltenbezeichner in Tabelle 1 & 2 für den Primärschlüssel sind identisch

      ADOXColumn := ADOXTable.Columns.Item['Spalte2'] as _Column;
      ADOXKey.Columns.Append(ADOXColumn.Name, ADOXColumn.Type_, ADOXColumn.DefinedSize);
      ADOXKey.Columns['Spalte2'].RelatedColumn := 'Spalte2';

      ADOXColumn := ADOXTable.Columns.Item['Spalte3'] as _Column;
      ADOXKey.Columns.Append(ADOXColumn.Name, ADOXColumn.Type_, ADOXColumn.DefinedSize);
      ADOXKey.Columns['Spalte3'].RelatedColumn := 'Spalte3';

      //Die nachfolgende Zeile müsste laut MSDN Hilfe wie folgt aufgerufen werden:
      ADOXTable.Keys.Append(ADOXKey); //FEHLER: Delphi 5 erwartet weitere Parameter
      finally
      ADOXKey := nil;
      end;
      ---</PRE>
      <BR>
      Gibt es da auch ein entsprechendes Beispiel in Ihrem neuen Buch

      Comment


      • #4
        Hallo,

        &gt;ADOXTable.Keys.Append(ADOXKey); //FEHLER: Delphi 5 erwartet weitere Parameter

        die Methode <b>Append</b> erwartet beim Zugriff über die frühe Bindung 5 Parameter, daher hat der Compiler mit der Fehlermeldung rein formal Recht:
        <pre>
        procedure Append(Item: OleVariant; Type_: KeyTypeEnum; Column: OleVariant;
        const RelatedTable: WideString; const RelatedColumn: WideString); safecall;
        </pre>
        Man darf aber jederzeit über die Typumwandlung via OleVariant auf die späte Bindung wechseln, so dass dank der optionalen Parameter das Microsoft-Beispiel für Visual Basic auch mit Delphi 1:1 nachgebaut werden kann:
        <pre>
        OleVariant(aTable.Keys).Append(ADOXKey);
        </pre>
        Ich habe das zwar nicht ausprobiert, aber es sollte funktionieren

        Comment


        • #5
          Danke, Herr Kosch! Der "Trick" mit dem OleVariant klappt super

          Comment

          Working...
          X