sql >> Database >  >> RDS >> PostgreSQL

Hoe implementeer je een veel-op-veel-relatie in PostgreSQL?

De SQL DDL-instructies (data definition language) kunnen er als volgt uitzien:

CREATE TABLE product (
  product_id serial PRIMARY KEY  -- implicit primary key constraint
, product    text NOT NULL
, price      numeric NOT NULL DEFAULT 0
);

CREATE TABLE bill (
  bill_id  serial PRIMARY KEY
, bill     text NOT NULL
, billdate date NOT NULL DEFAULT CURRENT_DATE
);

CREATE TABLE bill_product (
  bill_id    int REFERENCES bill (bill_id) ON UPDATE CASCADE ON DELETE CASCADE
, product_id int REFERENCES product (product_id) ON UPDATE CASCADE
, amount     numeric NOT NULL DEFAULT 1
, CONSTRAINT bill_product_pkey PRIMARY KEY (bill_id, product_id)  -- explicit pk
);

Ik heb een paar aanpassingen gedaan:

  • De n:m-relatie wordt normaal gesproken geïmplementeerd door een aparte tabel - bill_product in dit geval.

  • Ik heb serial toegevoegd kolommen als primaire surrogaatsleutels . Overweeg in Postgres 10 of later een IDENTITY kolom in plaats daarvan. Zie:

    • Tabellen veilig hernoemen met seriële primaire-sleutelkolommen
    • Tabelkolom automatisch verhogen
    • https://www.2ndquadrant.com/en/blog/postgresql-10-identity-columns/

    Ik raad dat ten zeerste aan, omdat de naam van een product nauwelijks uniek is (geen goede "natuurlijke sleutel"). Ook is het afdwingen van uniciteit en het verwijzen naar de kolom in externe sleutels doorgaans goedkoper met een integer van 4 bytes (of zelfs een 8-byte bigint ) dan met een string die is opgeslagen als text of varchar .

  • Gebruik geen namen van basisgegevenstypen zoals date als ID's . Hoewel dit mogelijk is, is het een slechte stijl en leidt het tot verwarrende fouten en foutmeldingen. Gebruik legale, niet-geciteerde identificatiecodes in kleine letters. Gebruik nooit gereserveerde woorden en vermijd indien mogelijk dubbele aanhalingstekens tussen hoofdletters.

  • "naam" is geen goede naam. Ik hernoemde de kolom van de tabel product om product te zijn (of product_name of vergelijkbaar). Dat is een betere naamgevingsconventie . Anders, wanneer u een aantal tabellen samenvoegt in een query - wat u vaak doet in een relationele database - je krijgt uiteindelijk meerdere kolommen met de naam "naam" en moet kolomaliassen gebruiken om de rommel op te lossen. Dat is niet handig. Een ander wijdverbreid anti-patroon zou gewoon "id" zijn als kolomnaam.
    Ik weet niet zeker wat de naam is van een bill zou zijn. bill_id zal in dit geval waarschijnlijk volstaan.

  • price is van gegevenstype numeric om gebroken getallen precies op te slaan zoals ze zijn ingevoerd (willekeurig precisietype in plaats van floating point type). Als je uitsluitend met hele getallen werkt, maak dan dat integer . U kunt bijvoorbeeld prijzen opslaan als cent .

  • Het amount ("Products" in uw vraag) gaat naar de koppelingstabel bill_product en is van het type numeric ook. Nogmaals, integer als u uitsluitend met gehele getallen werkt.

  • Je ziet de buitenlandse sleutels in bill_product ? Ik heb beide gemaakt om wijzigingen trapsgewijs te wijzigen:ON UPDATE CASCADE . Als een product_id of bill_id zou moeten veranderen, wordt de wijziging doorgevoerd in alle afhankelijke items in bill_product en er gaat niets kapot. Dat zijn slechts verwijzingen zonder eigen betekenis.
    Ik heb ook ON DELETE CASCADE gebruikt voor bill_id :Als een rekening wordt verwijderd, verdwijnen de details ervan.
    Niet zo voor producten:u wilt geen product verwijderen dat in een rekening wordt gebruikt. Postgres zal een foutmelding geven als u dit probeert. Je zou nog een kolom toevoegen aan product om in plaats daarvan verouderde rijen te markeren ("soft-delete").

  • Alle kolommen in dit basisvoorbeeld worden uiteindelijk NOT NULL , dus NULL waarden zijn niet toegestaan. (Ja, allemaal kolommen - primaire sleutelkolommen zijn gedefinieerd UNIQUE NOT NULL automatisch.) Dat komt omdat NULL waarden zouden in geen van de kolommen logisch zijn. Het maakt het leven van een beginner gemakkelijker. Maar je zult niet zo gemakkelijk wegkomen, je moet NULL begrijpen toch behandelen. Extra kolommen kunnen NULL allow toestaan waarden, functies en joins kunnen NULL introduce introduceren waarden in zoekopdrachten enz.

  • Lees het hoofdstuk over CREATE TABLE in de handleiding.

  • Primaire sleutels zijn geïmplementeerd met een unieke index op de sleutelkolommen, die query's met voorwaarden op de PK-kolom(men) snel maakt. De volgorde van sleutelkolommen is echter relevant in sleutels met meerdere kolommen. Sinds de PK op bill_product is op (bill_id, product_id) in mijn voorbeeld wil je misschien nog een index toevoegen op slechts product_id of (product_id, bill_id) als je vragen hebt op zoek naar een gegeven product_id en geen bill_id . Zie:

    • PostgreSQL samengestelde primaire sleutel
    • Is een samengestelde index ook goed voor zoekopdrachten op het eerste veld?
    • Werking van indexen in PostgreSQL
  • Lees het hoofdstuk over indexen in de handleiding.




  1. orakel varchar naar nummer

  2. Hoe een enkele MySQL-tabel te herstellen met mysqldump?

  3. Tips en trucs voor het implementeren van op databaserollen gebaseerde toegangscontroles voor MariaDB

  4. Retourneert resultaten van een sql-query als JSON in oracle 12c