มันมีเหตุผลที่จะทำเครื่องหมายคอลัมน์ทั้งหมด แต่เป็นหนึ่งในคีย์หลัก?


9

ฉันมีโต๊ะที่เป็นตัวแทนของภาพยนตร์ เขตข้อมูลคือ:
id (PK), title, genre, runtime, released_in, tags, origin, downloads.

ฐานข้อมูลของฉันไม่สามารถปนเปื้อนด้วยแถวที่ซ้ำกันดังนั้นฉันต้องการบังคับใช้ซ้ำ ปัญหาคือว่าภาพยนตร์ที่แตกต่างกันอาจมีชื่อเดียวกันหรือแม้กระทั่งเขตเดียวกันยกเว้นและtags downloadsวิธีการบังคับใช้เอกลักษณ์?

ฉันคิดถึงสองวิธี:

  • สร้างฟิลด์ทั้งหมดยกเว้นdownloadsคีย์หลัก ฉันติดตามdownloadsเพราะมันเป็น JSON และอาจส่งผลกระทบต่อประสิทธิภาพการทำงาน
  • เก็บidเป็นคีย์หลักเท่านั้น แต่เพิ่มข้อ จำกัด ที่ไม่ซ้ำกับคอลัมน์อื่น ๆ ทั้งหมด (ยกเว้นอีกครั้งdownloads)

ฉันอ่านคำถามซึ่งคล้ายกันมาก แต่ฉันไม่เข้าใจว่าฉันควรทำอย่างไร ขณะนี้ตารางนี้ไม่เกี่ยวข้องกับตารางอื่น ๆ แต่ในอนาคตอาจเป็น

ในขณะนี้ฉันมีบันทึกน้อยกว่า 20,000 รายการเล็กน้อย แต่ฉันคาดว่าจำนวนจะเพิ่มขึ้น ฉันไม่รู้ว่าสิ่งนี้เกี่ยวข้องกับปัญหาหรือไม่

แก้ไข:ฉันแก้ไขสคีมาและนี่คือวิธีที่ฉันจะสร้างตาราง:

CREATE TABLE movies (
    id          serial PRIMARY KEY,
    title       text NOT NULL,
    runtime     smallint NOT NULL CHECK (runtime >= 0),
    released_in smallint NOT NULL CHECK (released_in > 0),
    genres      text[] NOT NULL default ARRAY[]::text[],
    tags        text[] NOT NULL default ARRAY[]::text[],
    origin      text[] NOT NULL default ARRAY[]::text[],
    downloads   json NOT NULL,
    inserted_at timestamp NOT NULL default current_timestamp,
    CONSTRAINT must_be_unique UNIQUE(title,runtime,released_in,genres,tags,origin)
);

ฉันเพิ่มtimestampคอลัมน์ด้วย แต่นั่นไม่ใช่ปัญหาเพราะฉันจะไม่แตะต้องมัน ดังนั้นจึงเป็นไปโดยอัตโนมัติและไม่ซ้ำใคร


คำถามที่เกี่ยวข้องอย่างใกล้ชิด (พร้อมคำตอบ) บน SO: ฉันต้องการคีย์หลักสำหรับตารางของฉันซึ่งมี UNIQUE (ประกอบด้วย 4 คอลัมน์) หนึ่งในนั้นสามารถเป็น NULL ได้หรือไม่ . ถ้าใด ๆ ของคอลัมน์ที่สามารถเป็นโมฆะพิจารณาอย่างเร่งด่วนนี้: dba.stackexchange.com/q/9759/3684
Erwin Brandstetter

คำตอบ:


4

นิยามตารางของคุณดูสมเหตุสมผลแล้ว กับคอลัมน์ทั้งหมดจำกัด จะทำงานตามที่คาด - ยกเว้นสำหรับความผิดพลาดและความแตกต่างเล็ก ๆ น้อย ๆ ในการสะกดซึ่งอาจจะค่อนข้างทั่วไปฉันกลัว พิจารณา@ คิดเห็นNOT NULLUNIQUE

ทางเลือกพร้อมดัชนีเฉพาะการใช้งาน

ตัวเลือกอื่นจะเป็นดัชนีเฉพาะที่ใช้งานได้ (คล้ายกับสิ่งที่@Dave แสดงความคิดเห็น ) แต่ฉันจะใช้uuidชนิดข้อมูลเพื่อปรับขนาดและประสิทธิภาพของดัชนีให้เหมาะสมที่สุด

การส่งจากอาร์เรย์ไปยังข้อความไม่ใช่IMMUTABLE(เนื่องจากการใช้งานทั่วไป):

ดังนั้นคุณต้องมีฟังก์ชันตัวช่วยเล็กน้อยเพื่อประกาศว่าไม่เปลี่ยนรูป:

CREATE OR REPLACE FUNCTION f_movie_uuid(_title text
                                      , _runtime int2
                                      , _released_in int2
                                      , _genres text[]
                                      , _tags text[]
                                      , _origin text[])
  RETURNS uuid LANGUAGE sql IMMUTABLE AS  -- faking IMMUTABLE
'SELECT md5(_title || _runtime::text || _released_in::text
         || _genres::text || _tags::text || _origin::text)::uuid';

ใช้สำหรับนิยามดัชนี:

CREATE UNIQUE INDEX movies_uni_idx
ON movies (f_movie_uuid(title,runtime,released_in,genres,tags,origin));

ซอ Fiddle

รายละเอียดเพิ่มเติม:

คุณอาจใช้ UUID ที่สร้างขึ้นเป็น PK แต่ฉันจะยังคงใช้serialคอลัมน์ที่มี 4 ไบต์ซึ่งง่ายและราคาถูกสำหรับการอ้างอิง FK และวัตถุประสงค์อื่น ๆ UUID จะเป็นตัวเลือกที่ยอดเยี่ยมสำหรับระบบกระจายที่จำเป็นต้องสร้างค่า PK อิสระ หรือสำหรับตารางที่มีขนาดใหญ่มาก แต่มีภาพยนตร์ในระบบสุริยะของเรามีไม่มากพอ

ข้อดีและข้อเสีย

ข้อ จำกัด ที่ไม่ซ้ำกันมีการใช้งานที่มีดัชนีที่ไม่ซ้ำกันในคอลัมน์ที่เกี่ยวข้อง ใส่คอลัมน์ที่เกี่ยวข้องในคำจำกัดความก่อนและคุณมีดัชนีที่มีประโยชน์สำหรับวัตถุประสงค์อื่น ๆ เพื่อเป็นหลักประกันผลประโยชน์

มีประโยชน์อื่น ๆ โดยเฉพาะนี่คือรายการ:

ดัชนีการทำงานที่ไม่ซ้ำกันคือขนาดที่เล็กลงซึ่งอาจทำให้เร็วขึ้นอย่างมาก หากคอลัมน์ของคุณไม่ใหญ่เกินไปความแตกต่างจะไม่มาก นอกจากนี้ยังมีต้นทุนค่าโสหุ้ยเล็กน้อยสำหรับการคำนวณ

concatenating คอลัมน์ทั้งหมดสามารถนำผลบวกปลอม ( 'foo ' || 'bar' = 'foob ' || 'ar'แต่ที่ดูเหมือนว่ามากไม่น่าสำหรับกรณีนี้. Typos มีมากขึ้นโอกาสที่คุณสามารถละเว้นได้ที่นี่

เอกลักษณ์และอาร์เรย์

อาร์เรย์จะต้องมีการจัดเรียงอย่างต่อเนื่องเพื่อให้ความรู้สึกที่ไม่ซ้ำกันในการจัดเรียงใด ๆ อาศัยผู้ประกอบการเนื่องจาก= '{1,2}' <> '{2,1}'ผมขอแนะนำให้ตารางมองขึ้นสำหรับgenre, tagและoriginกับserialPK และรายการที่ไม่ซ้ำกันซึ่งจะช่วยให้การค้นหาเลือนสำหรับองค์ประกอบมากมาย แล้ว:

ไม่ว่าจะด้วยวิธีใดการทำงานกับอาร์เรย์โดยตรงหรือด้วยสคีมาปกติและมุมมองที่เป็นรูปธรรมการค้นหาจะมีประสิทธิภาพมากด้วยดัชนีและตัวดำเนินการที่ถูกต้อง:

นอกเหนือ

หากคุณกำลังใช้ Postgres 9.4 หรือในภายหลังพิจารณาแทนjsonbjson


6

ลองนึกภาพคุณออกไปกับกลุ่มเพื่อนและการสนทนาเปลี่ยนเป็นภาพยนตร์ มีคนถามว่า "คุณคิดอย่างไรกับ 'The Three Musketeers'?" คุณตอบว่า "อันไหน"

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

คำตอบสำหรับคำถามของฉันและของคุณเหมือนกัน

อย่างไรก็ตามฉันจะไม่คิดว่าประเภทจะเป็นผู้สมัครที่ดี เหตุผลหนึ่งประเภทเป็นเกณฑ์อัตนัยมากเกินไป การกระทำ 'The Three Musketeers' หรือไม่ ละคร? การผจญภัย? ตลก? การผจญภัย? โรแมนติกคอมเมดี้? ฉันมักจะดูหนังเรื่องเดียวกันตามประเภทต่าง ๆ แม้ว่าคุณจะอนุญาตให้มีหลายประเภทผู้ใช้ของคุณอาจเลือกที่แตกต่างกันโดยสิ้นเชิงที่ไม่ได้อยู่ในรายชื่อภาพยนตร์ที่พวกเขากำลังค้นหา

แม้เวลาทำงานอาจแตกต่างกันโดยเฉพาะระหว่างโรงละครและรุ่น VCR / DVD / b-ray

ดังนั้นคุณต้องมีคุณสมบัติที่ยากต่อวัตถุประสงค์ซึ่งจะไม่เปลี่ยนจากสื่อหนึ่งไปสู่อีกสื่อหนึ่ง น่าเสียดายที่สามารถยกเว้นชื่อของภาพยนตร์ได้เนื่องจากทราบว่ามีการเปลี่ยนชื่อภาพยนตร์โดยเฉพาะหลังจากการเผยแพร่ภาคต่อ

แล้ววันที่วางจำหน่ายล่ะ การแสดงละครในปี 1993? VCR รุ่นปี 1999 การเปิดตัวดีวีดีของปี 2004? คุณได้รับความคิด

มาคิดดูหนังเรื่องไหนที่กำกับโดย Alan Smithee? ในที่สุดผู้กำกับตัวจริงเคยก้าวไปข้างหน้าเพื่อตั้งชื่อโครงการนี้หลังจากข้อเท็จจริงหรือไม่? ฉันไม่รู้

อืมฉันควรหยุดในขณะที่ยังมีเกณฑ์เหลืออยู่

บางจุดเพิ่มเติม:

  • ใช่เก็บคีย์ตัวแทนและสร้างดัชนีที่ไม่ซ้ำกันในฟิลด์คีย์ธรรมชาติ (ถ้าคุณสามารถตอกหมุดเหล่านั้นได้) คีย์ตัวแทนจะดีที่สุดสำหรับการอ้างอิงคีย์ต่างประเทศ คุณไม่ต้องการทำซ้ำฟิลด์คีย์ธรรมชาติทั้งหมดในทุกตารางที่มีการอ้างอิงถึงภาพยนตร์
  • ดร็อปฟิลด์ฟิลด์ (ประเภทแท็กต้นกำเนิด) ไปข้างหน้าและทำให้ปกติคุณลักษณะเหล่านั้นเป็นปกติ ฉันไม่เคยเห็นเขตข้อมูลอาร์เรย์ที่ไม่ได้เป็นปัญหามากเกินกว่าที่จะคุ้มค่าโดยเฉพาะอย่างยิ่งถ้าคุณต้องการให้พวกเขาสามารถค้นหาได้ ("... ที่ประเภท = 'สยองขวัญ' ... ") หมายเหตุนี้จะไม่ได้โดยอัตโนมัติขจัดปัญหาใด ๆ กับความแตกต่างกรณีและการสะกดคำ ( "นิยายวิทยาศาสตร์" VS "SciFi") - เว้นแต่คุณจะรักษาอย่างถูกต้องตารางการค้นหา แต่มันง่ายกว่ามากในการตรวจสอบความแตกต่างดังกล่าวในเขตข้อมูลหนึ่งของตารางขนาดเล็กกว่าทุกเซลล์อาร์เรย์ของทุกแถวของตารางขนาดใหญ่

4

คอลัมน์ ID ไม่มีข้อได้เปรียบเลยเมื่อกล่าวถึงความเป็นเอกลักษณ์ที่คุณต้องการ / จำเป็นในการบังคับใช้ เอกลักษณ์ของการรวมกันของคุณลักษณะใด ๆ จะไม่ถูกบังคับใช้โดยการเพิ่ม ID ที่ไม่มีความหมาย "ข้อได้เปรียบ" ของมันจะแสดงเฉพาะเมื่อคุณไปถึงจุดที่คุณต้องการตารางใหม่ที่ต้องการรหัสต่างประเทศของตารางนี้ ในกรณีนั้นและหากคุณรวมรหัสแล้วคุณสามารถใช้รหัสนั้นเป็น FK ในตารางใหม่ของคุณ (แต่อย่าคิดว่ามันจะเป็นอาหารกลางวันฟรีข้อเสียของวิธีการเช่นนี้คือคุณจะพบว่าคุณเขียนตัวเองมากขึ้นเพื่อจุดประสงค์ในการดึงข้อมูลที่สมบูรณ์อาจเป็นส่วนหนึ่งของตารางใหม่ที่คุณทำ )


1
หากกฎทางธุรกิจบอกว่าการรวมกันของค่าในแอตทริบิวต์ FOO และ BAR จะต้องไม่ซ้ำกันดังนั้นการเพิ่ม ID จะไม่ประสบความสำเร็จ การเพิ่ม ID เพียงช่วยอำนวยความสะดวกในการหลีกเลี่ยงการรวม FOO และ BAR เช่นในตารางอ้างอิง ซึ่งจำเป็นต้องเข้าร่วมมากขึ้นเนื่องจากแอตทริบิวต์ FOO และ BAR (ซึ่งมีตัวระบุธุรกิจ) ไม่ใช่ตำแหน่งที่พวกเขาสามารถทำได้ (และสถานที่ที่พวกเขาคาดว่าน่าจะเป็นอย่างน้อยจากมุมมองทางธุรกิจ)
เออร์วิน Smout

1
ไม่ใช่ "แถว" ที่ต้องไม่ซ้ำกัน แต่เป็นสิ่งที่ธุรกิจบอกว่าเป็นตัวระบุที่ต้องเป็น หากเป็นการรวมกันของแอตทริบิวต์ FOO และ BAR แสดงว่าเป็นการรวมกันของแอตทริบิวต์ FOO และ BAR
เออร์วิน Smout

2
การมีรหัสหรือไม่สามารถแก้ปัญหาการบังคับใช้ความไม่ซ้ำกันของคอลัมน์ "ธุรกิจ" ในตารางของคุณ การบังคับใช้ความเป็นเอกลักษณ์จะต้องทำโดยการประกาศปุ่มที่เหมาะสม (ซึ่งคุณทำ - ความจริงที่ว่าคุณใช้คำว่า "CONSTRAINT" แทนคำว่า "CONSTRAINT" แทน "KEY" ไม่ได้หมายความว่ามันไม่ใช่กุญแจ)
เออร์วิน Smout
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.