มันเสียเปล่าไหมที่จะสร้างตารางฐานข้อมูลใหม่แทนที่จะใช้ชนิดข้อมูล enum?


38

สมมติว่าฉันมีบริการ 4 ประเภทที่ฉันเสนอให้ (พวกเขาไม่น่าจะเปลี่ยนแปลงบ่อย):

  • การทดสอบ
  • ออกแบบ
  • การเขียนโปรแกรม
  • อื่น ๆ

สมมติว่าฉันมีบริการจริง 60-80 รายการที่แต่ละบริการจัดอยู่ในหมวดหมู่ข้างต้น ตัวอย่างเช่น 'บริการ' สามารถเป็น "โปรแกรมทดสอบโดยใช้เทคนิค A" และเป็นประเภท "การทดสอบ"

ฉันต้องการเข้ารหัสให้เป็นฐานข้อมูล ฉันมากับตัวเลือกไม่กี่:

ตัวเลือก 0:

ใช้VARCHARโดยตรงเพื่อเข้ารหัสประเภทบริการโดยตรงเป็นสตริง

ตัวเลือกที่ 1:

enumฐานข้อมูลการใช้งาน แต่enum นั้นชั่วร้าย

ตัวเลือก 2:

ใช้สองตาราง:

service_line_item (id, service_type_id INT, description VARCHAR);
service_type (id, service_type VARCHAR);

ฉันยังสามารถเพลิดเพลินกับ Referential Integrity:

ALTER service_line_item 
    ADD FOREIGN KEY (service_type_id) REFERENCES service_type (id);

ฟังดูดีใช่มั้ย

แต่ฉันยังคงต้องเข้ารหัสสิ่งต่าง ๆ และจัดการกับจำนวนเต็มเช่นเมื่อเติมตาราง หรือฉันต้องสร้างการเขียนโปรแกรมอย่างละเอียดหรือสร้าง DB เมื่อเติมหรือจัดการกับตาราง กล่าวคือเข้าร่วมเมื่อจัดการกับฐานข้อมูลโดยตรงหรือสร้างเอนทิตีวัตถุที่มุ่งเน้นใหม่ในด้านการเขียนโปรแกรมและทำให้แน่ใจว่าฉันทำงานได้อย่างถูกต้อง

ตัวเลือก 3:

อย่าใช้enumอย่าใช้สองตาราง แต่เพียงใช้คอลัมน์จำนวนเต็ม

service_line_item (
    id,
    service_type INT,        -- use 0, 1, 2, 3 (for service types)
    description VARCHAR
);

นี่เป็นเหมือน 'ปลอม enum' ที่ต้องการค่าใช้จ่ายมากขึ้นในด้านโค้ดของสิ่งต่าง ๆ เช่นรู้{2 == 'Programming'}และจัดการกับมันอย่างเหมาะสม

คำถาม:

ขณะนี้ฉันได้นำไปใช้โดยใช้ตัวเลือกที่ 2ภายใต้แนวคิด

  1. ห้ามใช้ enum (ตัวเลือก 1)
  2. หลีกเลี่ยงการใช้ฐานข้อมูลเป็นสเปรดชีต (ตัวเลือก 0)

แต่ฉันอดไม่ได้ที่จะรู้สึกว่ามันไร้ประโยชน์สำหรับฉันในแง่ของการเขียนโปรแกรมและค่าใช้จ่ายด้านความคิด - ฉันต้องระวังสองตารางและจัดการกับสองตารางเทียบกับหนึ่ง

สำหรับ 'วิธีที่สิ้นเปลืองน้อย' Option 3ฉันกำลังมองหาที่ ไอทีมีน้ำหนักเบาและจำเป็นต้องใช้รหัสเดียวกันเพื่อสร้างการทำงาน (ด้วยการปรับเปลี่ยนเล็กน้อย แต่ความซับซ้อนและโครงสร้างนั้นเหมือนกัน แต่มีโต๊ะเดียว)

ฉันคิดว่ามันไม่สิ้นเปลืองเสมอไปและมีกรณีที่ดีสำหรับตัวเลือกทั้งสอง แต่มีแนวทางที่ดีว่าควรใช้ตัวเลือกที่ 2 และตัวเลือกที่ 3 เมื่อใด

เมื่อมีเพียงสองประเภท (ไบนารี)

ในการเพิ่มคำถามนี้อีกเล็กน้อย ... ในสถานที่เดียวกันฉันมีตัวเลือกไบนารีของบริการ "มาตรฐาน" หรือ "ยกเว้น" ซึ่งสามารถใช้กับรายการโฆษณาบรรทัดบริการได้ ฉันได้เข้ารหัสว่าการใช้ตัวเลือกที่ 3

ฉันเลือกที่จะไม่สร้างตารางใหม่เพื่อเก็บค่า {"Standard", "Exception"} ดังนั้นคอลัมน์ของฉันเพิ่งจะถือ {0, 1} และชื่อคอลัมน์ของฉันถูกเรียกexceptionและรหัสของฉันกำลังทำการแปลจาก{0, 1} => {STANDARD, EXCEPTION}(ซึ่งฉันเข้ารหัสเป็นค่าคงที่ในภาษาการเขียนโปรแกรม)

จนถึงตอนนี้ไม่ชอบวิธีนั้น ..... (ไม่ใช่ตัวเลือกที่ชื่นชอบ 2 หรือตัวเลือก 3) ฉันค้นหาตัวเลือก 2 ที่เหนือกว่า 3 แต่มีค่าใช้จ่ายมากกว่าและยังคงฉันไม่สามารถหลีกเลี่ยงการเข้ารหัสสิ่งต่าง ๆ เป็นจำนวนเต็มไม่ว่าตัวเลือกใดที่ฉันใช้จาก 2 และ 3

ออม

ในการเพิ่มบริบทหลังจากอ่านคำตอบ - ฉันเพิ่งเริ่มใช้ ORM อีกครั้ง (เมื่อเร็ว ๆ นี้) ในกรณีของฉัน Doctrine 2 หลังจากกำหนด DB schema ผ่านคำอธิบายประกอบฉันต้องการเติมฐานข้อมูล เนื่องจากชุดข้อมูลทั้งหมดของฉันมีขนาดค่อนข้างเล็กฉันจึงอยากลองใช้การเขียนโปรแกรมโครงสร้างเพื่อดูว่ามันทำงานอย่างไร

ฉันเติมข้อมูลservice_types ก่อนแล้วจึงservice_line_items เนื่องจากมีรายการที่มีอยู่จากสเปรดชีตจริง ดังนั้นสิ่งต่างๆเช่น 'มาตรฐาน / ข้อยกเว้น' และ 'การทดสอบ' จึงเป็นสตริงทั้งหมดในสเปรดชีตและจะต้องเข้ารหัสเป็นประเภทที่เหมาะสมก่อนที่จะเก็บไว้ในฐานข้อมูล

ฉันพบคำตอบ SO นี้: คุณใช้อะไรแทน ENUM ในหลักคำสอน 2 ซึ่งแนะนำให้ไม่ใช้การสร้าง Enum ของ DB แต่ใช้INTฟิลด์และเข้ารหัสชนิดโดยใช้โครงสร้าง 'const' ของภาษาโปรแกรม

แต่ดังที่ระบุไว้ในคำถาม SO ข้างต้นฉันสามารถหลีกเลี่ยงการใช้จำนวนเต็มโดยตรงและใช้โครงสร้างภาษา - ค่าคงที่ - เมื่อมีการกำหนด ....

แต่ยังคง .... ไม่ว่าคุณจะเลี้ยวอย่างไรถ้าฉันเริ่มต้นด้วยstringประเภทฉันต้องแปลงมันเป็นประเภทที่เหมาะสมก่อนแม้ว่าจะใช้ ORM

ดังนั้นถ้าพูดว่า$str = 'Testing';ฉันยังต้องมีบล็อกอยู่ที่ไหนสักแห่งที่ทำสิ่งที่ชอบ:

switch($str):
{ 
    case 'Testing':  $type = MyEntity::TESTING; break;
    case 'Other':    $type = MyEntity::OTHER; break;
}

สิ่งที่ดีคือคุณไม่ได้จัดการกับจำนวนเต็ม / เลขเวทย์มนตร์ [แทนการจัดการกับปริมาณคงที่ที่เข้ารหัส] แต่สิ่งที่เลวร้ายคือคุณไม่สามารถดึงสิ่งที่เข้าและออกจากฐานข้อมูลโดยอัตโนมัติโดยไม่มีขั้นตอนการแปลงนี้ ความรู้

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


9
ไม่ว่าคุณจะทำอะไรอย่าทำ # 3 นักจิตวิทยารักษามันอย่างต่อเนื่องจะต้องคิดออกว่าตัวเลขมายากลหมายถึงอะไร ถ้าคุณทำอย่างนั้นคุณก็หวังว่าพวกเขาจะไม่รู้ว่าคุณอยู่ที่ไหน blog.codinghorror.com/coding-for-violent-psychopaths
RubberDuck

7
ฉันชอบตัวเลือกที่ 2 หากคุณไม่ชอบการเพิ่มจำนวนของตารางการค้นหาให้ใช้หนึ่งตารางและเพิ่มคอลัมน์ "ประเภทการค้นหา" แต่ใช่แล้วการสร้างตารางการค้นหาเป็นวิธี "มาตรฐาน" ในการทำเช่นนี้เพราะช่วยให้คุณสามารถทำสิ่งที่สนุกสนานได้อย่างง่ายดาย
Robert Harvey

อย่าใช้ "แก้ไข" ในโพสต์ของคุณที่นี่; เราไม่ได้เป็นฟอรั่ม โพสต์ Stack Exchange ทุกรายการมีประวัติการแก้ไขโดยละเอียดที่ทุกคนสามารถดูได้
Robert Harvey

ถ้าฉันไม่สามารถใช้แก้ไขฉันจะใช้อะไรดี
เดนนิส

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

คำตอบ:


35

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

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

ตัวเลือกอื่น ๆ ทั้งหมดที่คุณอธิบายคือเฉพาะกิจและแฮ็ครวมถึง MySQL enumเพราะมันไม่ได้เป็นส่วนหนึ่งของมาตรฐาน SQL (นอกเหนือจากนั้นสิ่งที่แย่มากenumคือการนำ MySQL ไปใช้ไม่ใช่ความคิดฉันไม่รังเกียจที่จะเห็นมันในวันหนึ่งซึ่งเป็นส่วนหนึ่งของมาตรฐาน)

ตัวเลือกสุดท้ายของคุณ # 3 ด้วยการใช้จำนวนเต็มล้วนเป็นแฮ็คโดยเฉพาะ คุณได้รับสิ่งที่เลวร้ายที่สุดของโลกทั้งหมด: ไม่มีความสมบูรณ์ของการอ้างอิงไม่มีค่าชื่อไม่มีความรู้ที่ชัดเจนภายในฐานข้อมูลของสิ่งที่ค่าหมายถึงเพียงจำนวนเต็มโดยพลการโยนทั่วทุกสถานที่ โดยโทเค็นนี้คุณอาจเลิกใช้ค่าคงที่ในรหัสของคุณและเริ่มใช้ค่าตายตัวแทน circumference = radius * 6.28318530718;. แล้วเรื่องนั้นล่ะ

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

ประโยคของคุณเกี่ยวกับการ "เข้ารหัสสิ่งต่าง ๆ และจัดการกับจำนวนเต็ม" หรือต้อง "สร้างโครงสร้างการเขียนโปรแกรมที่ซับซ้อน" หรือ "การสร้างเอนทิตีวัตถุเชิงใหม่บนฝั่งการเขียนโปรแกรม" บอกฉันว่าบางทีคุณอาจพยายามทำวัตถุเชิงสัมพันธ์ การแมป (ORM) บน fly กระจายไปทั่วโค้ดของแอปพลิเคชันของคุณหรือในกรณีที่ดีที่สุดคุณอาจพยายามหมุนกลไกการแม็พสัมพันธ์เชิงวัตถุของคุณเองแทนที่จะใช้เครื่องมือ ORM ที่มีอยู่สำหรับงานเช่น Hibernate ทุกสิ่งเหล่านี้เป็นเรื่องง่ายที่มีไฮเบอร์เนต ใช้เวลาสักครู่ในการเรียนรู้ แต่เมื่อคุณได้เรียนรู้แล้วคุณสามารถมุ่งเน้นไปที่การพัฒนาแอปพลิเคชันของคุณและลืมเกี่ยวกับกลไก nitty gritty ของวิธีการแสดงข้อมูลในฐานข้อมูล

ท้ายที่สุดถ้าคุณต้องการทำให้ชีวิตของคุณง่ายขึ้นเมื่อทำงานกับฐานข้อมูลโดยตรงมีอย่างน้อยสองสิ่งที่คุณสามารถทำได้ฉันคิดได้ตอนนี้:

  1. สร้างมุมมองที่เข้าร่วมตารางหลักของคุณกับสิ่งที่พวกเขาอ้างอิงตารางอ้างอิงเพื่อให้แต่ละแถวมีไม่เพียง แต่รหัสการอ้างอิง แต่ยังชื่อที่สอดคล้องกัน

  2. แทนที่จะใช้รหัสจำนวนเต็มสำหรับตารางอ้างอิงให้ใช้คอลัมน์ CHAR (4) พร้อมตัวย่อ 4 ตัวอักษร ดังนั้นรหัสของหมวดหมู่ของคุณจะกลายเป็น "ทดสอบ", "DSGN", "PROG", "OTHR" ( คำอธิบายของพวกเขาจะยังคงเป็นคำภาษาอังกฤษที่เหมาะสมแน่นอน) มันจะช้าลงเล็กน้อย แต่เชื่อฉันไม่มีใครจะสังเกตเห็น

ในที่สุดเมื่อมีเพียงสองประเภทคนส่วนใหญ่ก็ใช้คอลัมน์บูลีน ดังนั้นคอลัมน์ "มาตรฐาน / ข้อยกเว้น" จะถูกนำมาใช้เป็นบูลีนและจะเรียกว่า "IsException"


3
นอกเหนือจากนี้Postgres ก็มีประเภท enumด้วยเช่นกัน มันเรียบง่ายและไม่มีอะไรพิเศษช่วยให้คุณสามารถใช้สตริงที่อ่านได้เป็นค่า แต่มีจำนวนเต็มที่มีประสิทธิภาพมากกว่าที่จะใช้ภายใต้ประทุน
Kat

สิ่งที่เกี่ยวกับกรณีที่ข้อมูลซ้ำแล้วซ้ำอีก แต่ไม่ซ้ำซ้อน (เช่นจะไม่ส่งผลให้เกิดความผิดปกติ update / แทรก / ลบ) ตัวอย่างเช่นเพศของบุคคล (ไม่น่าจะแนะนำประเภทข้อมูลใหม่ไม่จำเป็นต้องเปลี่ยนชื่อเพศ ฯลฯ )
Adam Thompson

สิ่งนี้: เพราะในที่สุดคุณจะพบว่าคุณต้องการ "สภาพแวดล้อมที่ยอมรับได้" และคุณไม่จำเป็นต้องเปลี่ยน enums
Pieter B

3

ตัวเลือก 2 ที่มีค่าคงที่หรือ enums ที่ส่วนท้ายการตั้งโปรแกรม
แม้ว่ามันจะซ้ำซ้อนกับความรู้ซึ่งเป็นการละเมิดหลักการ Single Source Of Truth แต่คุณสามารถจัดการกับมันได้โดยใช้เทคนิคFail-fast เมื่อระบบของคุณโหลดจะตรวจสอบว่าค่า enums หรือ const มีอยู่ในฐานข้อมูล หากไม่เป็นเช่นนั้นระบบจะแสดงข้อผิดพลาดและปฏิเสธที่จะโหลด โดยทั่วไปจะมีราคาถูกกว่าในการแก้ไขข้อผิดพลาดนี้ในเวลาต่อมาเมื่อมีสิ่งที่ร้ายแรงกว่าเกิดขึ้น


0

ไม่มีอะไรจะหยุดคุณโดยใช้สตริง [สั้น ๆ ] เป็นคีย์ดังนั้นคุณยังสามารถอ่านชื่อในตารางของคุณได้และไม่ใช้การเข้ารหัสตัวเลขตัวแทนที่ไม่มีความหมาย คุณควรจะมีตารางแยกต่างหากเพื่ออธิบายประเภทบริการเพียงแค่ในโอกาสที่ใบสมัครของคุณไปต่างประเทศ!

ผู้ใช้ของคุณสามารถเห็นสี่หมวดหมู่ของคุณในภาษาของตนเองแต่ตารางฐานข้อมูลของคุณยังคงมีค่าที่คุณสามารถอ่านได้ - และไม่มีประเภทใดที่ต้องการโครงสร้างฐานข้อมูลหรือการเปลี่ยนแปลงรหัส!

table service_type 
( id VARCHAR 
, name VARCHAR 
  primary key ( id ) 
);
table service_line_item 
( id 
, service_type VARCHAR 
, description VARCHAR
  foreign key ( service_type ) references service_type ( id )
);

select * from service_type ; 

+-------------+----------------+
| id          | name           |
+-------------+----------------+
| Testing     | Testen         |
| Design      | Design         | 
| Programming | Programmierung |
| Other       | Andere         |
+-------------+----------------+

หรือสำหรับลูกค้าชาวฝรั่งเศสของคุณ ...

update services_types set name = 'Essai'         where id = 'Testing'; 
update services_types set name = 'Conception'    where id = 'Design'; 
update services_types set name = 'Programmation' where id = 'Programming'; 
update services_types set name = 'Autre'         where id = 'Other'; 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.