จะใช้ความสัมพันธ์แบบกลุ่มต่อกลุ่มใน PostgreSQL ได้อย่างไร


104

ฉันเชื่อว่าชื่อเรื่องนี้อธิบายได้ในตัวเอง คุณสร้างโครงสร้างตารางใน PostgreSQL เพื่อสร้างความสัมพันธ์แบบกลุ่มต่อกลุ่มได้อย่างไร

ตัวอย่างของฉัน:

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

3
นำสินค้าออกจากตารางการเรียกเก็บเงินสร้างตารางใหม่ชื่อ "bill_products" โดยมีสองช่อง: ช่องหนึ่งชี้ไปที่ผลิตภัณฑ์และอีกช่องหนึ่งชี้ไปที่ใบเรียกเก็บเงิน ทำให้สองฟิลด์นั้นเป็นคีย์หลักของตารางใหม่
Marc B

ดังนั้น bill_products (บิลผลิตภัณฑ์); เหรอ? และทั้งสองคน PK?
Radu Ghe.Genu

1
ใช่. พวกเขาจะเป็น FK ทีละตัวชี้ไปที่โต๊ะของพวกเขาและพวกเขาจะรวมกันเป็น PK สำหรับตารางใหม่
Marc B

ดังนั้น bill_product (การอ้างอิงผลิตภัณฑ์ product.name, การอ้างอิงใบเรียกเก็บเงิน bill.name, (ผลิตภัณฑ์, บิล) คีย์หลัก)?
Radu Ghe ข้าม

พวกเขาจะชี้ไปที่ฟิลด์ PK ของตารางผลิตภัณฑ์และตารางบิลคืออะไร
Marc B

คำตอบ:


318

คำสั่ง SQL DDL (ภาษานิยามข้อมูล) อาจมีลักษณะดังนี้:

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

ฉันได้ทำการปรับเปลี่ยนเล็กน้อย:

  • โดยปกติความสัมพันธ์ n: mจะใช้ตารางแยกต่างหาก - bill_productในกรณีนี้

  • ฉันเพิ่มserialคอลัมน์เป็นคีย์หลักตัวแทน ใน Postgres 10 หรือใหม่กว่าให้พิจารณาIDENTITYคอลัมน์แทน ดู:

    ฉันขอแนะนำเป็นอย่างยิ่งเนื่องจากชื่อของผลิตภัณฑ์แทบจะไม่ซ้ำกันเลย (ไม่ใช่ "คีย์ธรรมชาติ" ที่ดี) นอกจากนี้การบังคับใช้เอกลักษณ์และอ้างอิงในคอลัมน์คีย์ต่างประเทศโดยทั่วไปจะมีราคาถูกกว่ากับ 4 ไบต์integer(หรือแม้กระทั่ง 8 ไบต์bigint) กว่าด้วยสตริงเก็บไว้เป็นหรือtextvarchar

  • อย่าใช้ชื่อของชนิดข้อมูลพื้นฐานเช่นdateเป็นตัวบ่งชี้ แม้ว่าจะเป็นไปได้ แต่ก็เป็นรูปแบบที่ไม่ดีและนำไปสู่ข้อผิดพลาดและข้อความแสดงข้อผิดพลาดที่สับสน ใช้กฎหมายกรณีที่ต่ำกว่าตัวระบุ unquoted อย่าใช้คำสงวนและหลีกเลี่ยงการใช้ตัวระบุกรณีแบบผสมที่ยกมาสองคำหากทำได้

  • "ชื่อ" ไม่ใช่ชื่อที่ดี ฉันเปลี่ยนชื่อคอลัมน์ของตารางproductเป็นproduct( product_nameหรือคล้ายกัน) นั่นคือการที่ดีกว่าการตั้งชื่อ มิฉะนั้นเมื่อคุณเข้าร่วมสองสามตารางในแบบสอบถามซึ่งคุณทำจำนวนมากในฐานข้อมูลเชิงสัมพันธ์คุณจะได้คอลัมน์หลายคอลัมน์ชื่อ "ชื่อ" และต้องใช้นามแฝงคอลัมน์เพื่อจัดระเบียบ นั่นไม่เป็นประโยชน์ รูปแบบการต่อต้านที่แพร่หลายอีกรูปแบบหนึ่งจะเป็นเพียง "id" เป็นชื่อคอลัมน์
    ฉันไม่แน่ใจว่าชื่อของ a billจะเป็นอย่างไร bill_idอาจจะพอเพียงในกรณีนี้

  • priceเป็นประเภทข้อมูล numericเพื่อจัดเก็บตัวเลขเศษส่วนอย่างแม่นยำตามที่ป้อน (ประเภทความแม่นยำโดยพลการแทนประเภทจุดลอยตัว) หากคุณจัดการกับจำนวนเต็มโดยเฉพาะให้ทำintegerเช่นนั้น ตัวอย่างเช่นคุณสามารถบันทึกราคาเป็นเซ็นต์

  • amount("Products"ในคำถามของคุณ) ไปลงในตารางการเชื่อมโยงbill_productและเป็นประเภทnumericได้เป็นอย่างดี อีกครั้งintegerหากคุณจัดการกับจำนวนเต็มโดยเฉพาะ

  • คุณเห็นคีย์ต่างประเทศในbill_product? ฉันสร้างทั้งสองเพื่อเรียงซ้อนการเปลี่ยนแปลง: ON UPDATE CASCADE. หาก a product_idหรือbill_idควรเปลี่ยนการเปลี่ยนแปลงจะเรียงซ้อนกันไปยังรายการทั้งหมดโดยbill_productไม่มีอะไรหยุดพัก สิ่งเหล่านี้เป็นเพียงการอ้างอิงโดยไม่มีความสำคัญของตัวเอง
    ฉันยังใช้ON DELETE CASCADEสำหรับbill_id: หากใบเรียกเก็บเงินถูกลบรายละเอียดจะตายไปด้วย
    ไม่ใช่สำหรับผลิตภัณฑ์: คุณไม่ต้องการลบผลิตภัณฑ์ที่ใช้ในใบเรียกเก็บเงิน Postgres จะแสดงข้อผิดพลาดหากคุณพยายามทำเช่นนี้ คุณจะต้องเพิ่มคอลัมน์อื่นproductเพื่อทำเครื่องหมายแถวที่ล้าสมัย ("soft-delete") แทน

  • คอลัมน์ทั้งหมดในขั้นพื้นฐานเช่นปลายขึ้นเพื่อเป็นNOT NULLดังนั้นNULLค่าไม่ได้รับอนุญาต (ใช่คอลัมน์ทั้งหมด - คอลัมน์คีย์หลักถูกกำหนดUNIQUE NOT NULLโดยอัตโนมัติ) นั่นเป็นเพราะNULLค่าต่างๆไม่สมเหตุสมผลในคอลัมน์ใด ๆ ทำให้ชีวิตของผู้เริ่มต้นง่ายขึ้น แต่คุณจะไม่หนีไปง่ายๆคุณต้องเข้าใจNULLการจัดการอยู่ดี คอลัมน์เพิ่มเติมอาจอนุญาตให้NULLค่าฟังก์ชันและการรวมสามารถแนะนำNULLค่าในแบบสอบถามเป็นต้น

  • อ่านบทที่ CREATE TABLEในคู่มือ

  • คีย์หลักถูกนำไปใช้กับดัชนีที่ไม่ซ้ำกันในคอลัมน์คีย์ซึ่งจะทำให้การสืบค้นมีเงื่อนไขในคอลัมน์ PK ทำได้อย่างรวดเร็ว อย่างไรก็ตามลำดับของคอลัมน์หลักมีความเกี่ยวข้องในคีย์หลายคอลัมน์ เนื่องจาก PK เปิดbill_productอยู่(bill_id, product_id)ในตัวอย่างของฉันคุณอาจต้องการเพิ่มดัชนีอื่นในเพียงproduct_idหรือ(product_id, bill_id)หากคุณมีข้อสงสัยมองหาที่กำหนดและไม่มีการproduct_id bill_idดู:

  • อ่านบทที่เกี่ยวกับการจัดทำดัชนีในคู่มือ


ฉันจะสร้างดัชนีสำหรับตารางการแมปได้bill_productอย่างไร โดยปกติควรมีลักษณะดังนี้: CREATE INDEX idx_bill_product_id ON booked_rates(bill_id, product_id). นี่ใช่มั้ย?
codyLine

1
@codyLine: ดัชนีนี้ถูกสร้างขึ้นโดยอัตโนมัติโดย PK
Erwin Brandstetter

1
@ErwinBrandstetter: ไม่ควรสร้างดัชนีใน bill_product สำหรับคอลัมน์ product_id?
คริสเตียน

2
@ ChristianB Almeida: มีประโยชน์ในหลาย ๆ กรณีใช่ ฉันเพิ่มเล็กน้อยเกี่ยวกับการจัดทำดัชนี
Erwin Brandstetter

1
@Jakov: มีเพียงอย่างเดียวคือ1billแถวสำหรับแต่ละการเรียกเก็บเงินในตาราง เราต้องการจำนวนต่อรายการที่เพิ่มเข้าbill_productมา
Erwin Brandstetter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.