PostgreSQL'de çoktan çoğa bir ilişki nasıl uygulanır?


102

Başlığın kendi kendini açıklayıcı olduğuna inanıyorum. PostgreSQL'de çoktan çoğa bir ilişki kurmak için tablo yapısını nasıl oluşturursunuz?

Örneğim:

Product(name, price);
Bill(name, date, Products);

2
fatura tablosundan ürünleri kaldırın, iki alana sahip "ürün_ürünler" adlı yeni bir tablo oluşturun: biri ürünleri işaret ediyor, diğeri faturayı gösteriyor. bu iki alanı bu yeni tablonun birincil anahtarı yapın.
Marc B

Yani bill_products (fatura, ürünler); ? Ve ikisi de PK?
Radu Gheorghiu

1
Evet. bireysel olarak kendi masalarına işaret eden bir FK olurlar ve birlikte yeni masanın PK'si olurlar.
Marc B

Peki, bill_product (ürün referansları ürün.adı, fatura referansları bill.name, (ürün, fatura) birincil anahtarı)?
Radu Gheorghiu

Ürün ve Fatura tablolarının PK alanlarının ne olacağına işaret ederler.
Marc B

Yanıtlar:


309

SQL DDL (veri tanımlama dili) ifadeleri şöyle görünebilir:

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
);

Birkaç düzenleme yaptım:

  • N: m ilişki normal olarak ayrı bir tablo tarafından uygulanan - bill_productbu durumda.

  • Vekil birincil anahtarlarserial olarak sütunlar ekledim . Postgres 10 veya sonraki sürümlerde bunun yerine bir sütun düşünün . Görmek:IDENTITY

    Bunu şiddetle tavsiye ediyorum, çünkü bir ürünün adı pek benzersiz değil (iyi bir "doğal anahtar" değil). Ayrıca, benzersizliği zorlamak ve sütuna yabancı anahtarlarda başvurmak, genellikle 4 bayt integer(veya hatta 8 bayt bigint) ile textveya olarak depolanan bir dizeden daha ucuzdur varchar.

  • Gibi temel veri tiplerinin isimlerini kullanmayın dateolarak tanımlayıcılar . Bu mümkün olsa da, kötü bir tarzdır ve kafa karıştırıcı hatalara ve hata mesajlarına yol açar. Kullanım hukuki, küçük harf, tırnaksız tanımlayıcılar . Asla ayrılmış sözcükler kullanmayın ve mümkünse çift tırnaklı karışık büyük / küçük harf tanımlayıcılardan kaçının.

  • "isim" iyi bir isim değil. Tablonun sütununu ( veya benzeri) productolarak yeniden adlandırdım . Bu daha iyi bir adlandırma kuralı . Aksi takdirde, ilişkisel bir veritabanında çok yaptığınız bir sorguda birkaç tabloyu birleştirdiğinizde, sonunda "ad" adlı birden çok sütunla karşılaşırsınız ve karışıklığı gidermek için sütun takma adlarını kullanmanız gerekir. Bu yardımcı olmuyor. Diğer bir yaygın anti-model, sütun adı olarak sadece "id" olacaktır. A'nın adının ne olacağından emin değilim . bu durumda muhtemelen yeterli olacaktır.productproduct_name
    billbill_id

  • pricetaşımaktadır veri türü numeric fraksiyonel numaralarını saklamak için tam olarak girilmesi (yerine kayan nokta türü keyfi hassas türü). Yalnızca tam sayılarla ilgileniyorsanız, bunu yapın integer. Örneğin, fiyatları Sent olarak kaydedebilirsiniz .

  • amount( "Products"Sorunuzu) bağlayan tabloya gider bill_productve tiptedir numericde. Yine, integeryalnızca tam sayılarla ilgileniyorsanız.

  • Görüyorsunuz yabancı anahtarları içinde bill_product? Ben kaskad değişikliklerine hem oluşturuldu: ON UPDATE CASCADE. Bir product_idveya bill_iddeğişmesi gerekiyorsa, değişiklik tüm bağlı girdilere basamaklanır bill_productve hiçbir şey bozulmaz. Bunlar, kendilerine ait önemi olmayan referanslardır.
    Ayrıca şunun ON DELETE CASCADEiçin de kullandım bill_id: Bir fatura silinirse, detayları onunla birlikte ölür.
    Ürünler için geçerli değil: Faturada kullanılan bir ürünü silmek istemezsiniz. Bunu denerseniz Postgres bir hata atar. Bunun productyerine eski satırları işaretlemek için başka bir sütun eklersiniz ("yazılımla silme").

  • Bu temel örnek uç up Tüm sütunlar olmak NOT NULLnedenle, NULLdeğerler izin verilmez. (Evet, tüm sütunlar - birincil anahtar sütunlar UNIQUE NOT NULLotomatik olarak tanımlanır .) Bunun nedeni NULL, sütunların hiçbirinde değerlerin anlamlı olmamasıdır. Yeni başlayanların hayatını kolaylaştırır. Ama bu kadar kolay kurtulamayacaksın, yine de NULLidare etmeyi anlamalısın . Ek sütunlar NULLdeğerlere, işlevlere ve birleştirmelere izin verebilir , NULLsorgulara vb. Değerler ekleyebilir .

  • CREATE TABLEKılavuzdaki bölümü okuyun .

  • Birincil anahtarlar , PK sütun (lar) ındaki koşullarla sorguları hızlı hale getiren, anahtar sütunlarında benzersiz bir dizin ile uygulanır . Ancak, anahtar sütunlarının sırası çok sütunlu anahtarlarla ilgilidir. Benim örneğimde PK açık bill_productolduğu için (bill_id, product_id), sadece üzerine başka bir dizin eklemek isteyebilirsiniz product_idveya (product_id, bill_id)verilen product_idve hayır arayan sorgularınız varsa bill_id. Görmek:

  • Kılavuzdaki dizinlerle ilgili bölümü okuyun .


Eşleme tablosu için nasıl bir dizin oluşturabilirim bill_product? Normalde görünüyor gibi olmalıdır: CREATE INDEX idx_bill_product_id ON booked_rates(bill_id, product_id). Bu doğru mu?
codyLine

1
@codyLine: Bu indeks PK tarafından otomatik olarak oluşturulur.
Erwin Brandstetter

1
@ErwinBrandstetter: product_id sütunu için bill_product üzerinde bir dizin oluşturulmamalı mı?
Hıristiyan

2
@ ChristianB.Al Almeida: Bu birçok durumda yararlıdır, evet. Endeksleme hakkında biraz ekledim.
Erwin Brandstetter

1
@Jakov: Tablodaki her fatura için sadece 1 satır var bill. Eklenen öğe başına miktara ihtiyacımız var bill_product.
Erwin Brandstetter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.