มันเป็นการปฏิบัติที่ไม่ถูกต้องหรือไม่ที่จะอนุญาตให้ผู้ใช้กำหนดเขตข้อมูล?


17

โดยทั่วไปแล้วจะถือว่าเป็นวิธีปฏิบัติที่ไม่ดีที่จะอนุญาตให้ผู้ใช้สร้างฟิลด์ในฐานข้อมูลสำหรับเว็บแอพหรือไม่?

ตัวอย่างเช่นฉันกำลังสร้างเว็บแอปสินค้าคงคลังที่บ้านสำหรับภรรยาของฉันและเธอจะต้องการกำหนดฟิลด์ของเธอเองสำหรับรายการที่แตกต่างกัน ฉันวางแผนที่จะอนุญาตให้เธอสร้างหมวดรายการและเพิ่ม "คุณสมบัติ" ให้กับหมวดหมู่เหล่านั้น คุณสมบัติจะเป็นคีย์ / ค่าที่จัดเก็บเป็นสตริง ถ้าเธอมีหมวดหมู่ที่เรียกว่า "Audio CDs" เธอสามารถเพิ่มฟีเจอร์สำหรับสิ่งต่าง ๆ เช่น "artist", "track" เป็นต้น แต่ในหมวดหมู่อื่นเช่น "เฟอร์นิเจอร์" เธอสามารถเพิ่มฟีเจอร์สำหรับสิ่งต่าง ๆ เช่น "วัสดุ "(ไม้พลาสติก ฯลฯ ) จากนั้นรายการใด ๆ อาจเป็นของหนึ่งหมวดหมู่ (หรือหลายกลุ่ม) เพิ่มคุณสมบัติเหล่านั้นไปยังรายการ

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

โดยทั่วไปแล้วผู้คนจะจัดการกับสิ่งนี้ใน "ชีวิตจริง" ได้อย่างไร?


4
คุณได้พิจารณาใช้ฐานข้อมูลที่มุ่งเน้นเอกสารเช่น MongoDB หรือไม่? คุณสามารถจัดเก็บเอกสารตามประเภทซึ่งทำหน้าที่เป็นสคีมาซึ่งสามารถแก้ไขได้ (อาจเป็นด้วยตนเองเนื่องจากโครงการขนาดเล็ก)
Andy Hunt

@AndyBursh หนึ่งในบิต 'fun' ที่มี postgres ปัจจุบันคือประเภทข้อมูล 'json' ( ลิงก์ ) วิธีการดังกล่าวจะช่วยให้ผู้ใช้สามารถจัดเก็บเขตข้อมูลที่ผู้ใช้ระบุในข้อมูล, er, เอกสาร, er, อะไรก็ตามแล้วใช้ส่วนที่เหลือของเขตข้อมูลสำหรับสิ่งที่คุณดัชนีที่เหมาะสมและชอบ แม้ว่าทั้งหมดนี้ขึ้นอยู่กับการใช้งานและมันยากที่จะบอกว่ามันจะทำงานได้ดีสำหรับแอปพลิเคชันเฉพาะหรือไม่ แต่มันเป็นสิ่งที่ต้องระวัง

ทั้งหมด: การอภิปรายที่ดีขอบคุณสำหรับความเข้าใจ! @ AndyBursh ฉันเคยได้ยิน MongoDB แล้ว แต่ไม่เคยอ่านมันเลย ดูเหมือนจะเป็นอีกหนึ่งโครงการบ้านที่ต้องทดลองกับ ...
zako42

คำตอบ:


19

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

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

พิจารณาตารางต่อไปนี้:

  + + --------------
  | สิ่ง |
  | -------------- |
  | id |
  | ประเภท |
  | desc |
  | attr1 |
  | attr2 |
  | attr3 |
  | attr4 |
  | attr5 |
  + + --------------

นี่คือหลังจากที่คุณได้เพิ่มคุณสมบัติบางอย่าง แทนการattr1แสร้งทำเป็นว่ามันอ่านartistหรือtracksหรือgenreหรืออะไรก็ตามแอตทริบิวต์สิ่งที่มี และแทนที่จะเป็น 5 จะเกิดอะไรขึ้นถ้าเป็น 50 เห็นได้ชัดว่าไม่สามารถจัดการได้ นอกจากนี้ยังต้องการการอัปเดตโมเดลและการปรับใช้แอปพลิเคชันอีกครั้งเพื่อจัดการฟิลด์ใหม่ ไม่เหมาะ

พิจารณาโครงสร้างตารางต่อไปนี้:

  + -------------- + + --------------- + + ------------- +
  | สิ่ง | | สิ่ง | attr |
  | -------------- | | --------------- | | ------------- |
  | id | <--- + | thing_id (fk) | +> | id |
  | ประเภท | | attr_id (fk) | + - + | ชื่อ |
  | desc | | ค่า | | |
  + -------------- + + --------------- + + ------------- +

คุณได้สิ่งที่มีพื้นฐานของมัน คุณมีอีกสองตาราง หนึ่งที่มีคุณสมบัติ แต่ละเขตข้อมูลเป็นแถวในattrตาราง แล้วก็มีthing_attrปุ่มต่างประเทศคู่หนึ่งที่สัมพันธ์กับthingตารางและattrโต๊ะ และนี่มีเขตข้อมูลค่าที่คุณเก็บค่าใด ๆ ของเขตข้อมูลสำหรับเอนทิตีนั้น

และตอนนี้คุณมีโครงสร้างที่สามารถอัปเดตตาราง attr ที่รันไทม์และสามารถเพิ่มฟิลด์ใหม่ (หรือลบ) ได้ทันทีโดยไม่กระทบกับแอปพลิเคชันโดยรวมอย่างมีนัยสำคัญ

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

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


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

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

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

  • คุณกำลังเขียนโปรแกรมเมตาดาต้า แทนที่จะสามารถแมปคอลัมน์นี้กับฟิลด์นั้นด้วย ORM ที่ดีคุณอาจทำสิ่งต่าง ๆ เช่นselect *แล้วทำโค้ดที่ซับซ้อนเพื่อค้นหาว่าข้อมูลคืออะไรจริง ๆ (ดูที่ResultSetMetaData ของ Java ) จากนั้นเก็บไว้ในแผนที่ ( หรือประเภทข้อมูลอื่น - แต่ไม่ใช่เขตข้อมูลที่ดีในรหัส) สิ่งนี้จะละทิ้งประเภทของงานพิมพ์และความปลอดภัยของการพิมพ์ผิดที่คุณมีอยู่ด้วยวิธีการดั้งเดิม
  • คุณน่าจะละทิ้ง ORM ซึ่งหมายความว่าคุณกำลังเขียน sql ดิบสำหรับรหัสทั้งหมดแทนที่จะปล่อยให้ระบบทำงานแทนคุณ
  • คุณเลิกใช้การอัปเกรดที่สะอาดหมดจดแล้ว จะเกิดอะไรขึ้นเมื่อลูกค้าเพิ่มเขตข้อมูลด้วยชื่อเดียวที่รุ่นถัดไปของคุณใช้ด้วย ในเว็บไซต์จับคู่การอัปเกรดที่ต้องการเพิ่มhasdateเขตข้อมูลสำหรับการจัดเก็บเวลาประทับได้ถูกกำหนดไว้แล้วเช่นเดียวhasdateกับบูลีนสำหรับการจับคู่ที่ประสบความสำเร็จ ... และตัวแบ่งการอัปเกรดของคุณ
  • คุณเชื่อมั่นว่าลูกค้าจะไม่ทำลายระบบโดยใช้คำที่สงวนไว้บางคำซึ่งทำลายการสืบค้นของคุณด้วย ... บางแห่ง
  • คุณผูกพันกับฐานข้อมูลหนึ่งแบรนด์ DDLของฐานข้อมูลที่แตกต่างกันจะแตกต่างกัน ประเภทฐานข้อมูลเป็นตัวอย่างที่ง่ายที่สุดของสิ่งนี้ varchar2vs textและไม่ชอบ รหัสของคุณเพื่อเพิ่มคอลัมน์จะทำงานบน MySQL แต่ไม่ใช่ Postgres หรือ Oracle หรือ SQL Server
  • คุณให้ความไว้วางใจของลูกค้าที่จริงเพิ่มข้อมูลดี ? แน่นอนว่า EAV นั้นห่างไกลจากอุดมคติ แต่ตอนนี้คุณมีชื่อตารางที่คลุมเครือที่น่ากลัวซึ่งคุณผู้พัฒนาไม่ได้เพิ่มด้วยดัชนีชนิดผิด (ถ้ามี) โดยไม่มีข้อ จำกัด ในรหัสที่จำเป็นต้อง เป็นและอื่น ๆ
  • คุณได้รับสิทธิ์การแก้ไขสคีมาให้กับผู้ใช้ที่เรียกใช้แอปพลิเคชัน Bobby Drop Tables เล็ก ๆ น้อย ๆเป็นไปไม่ได้เมื่อคุณถูก จำกัด ให้ใช้ SQL แทนที่จะเป็น DDL (แน่นอนว่าคุณสามารถทำdelete * from studentsแทนได้ จำนวนของสิ่งต่าง ๆ ที่ผิดไปจากการเข้าถึงสคีมาทั้งจากอุบัติเหตุหรือกิจกรรมที่เป็นอันตราย

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


4
คุณได้สร้างฐานข้อมูลขึ้นใหม่ด้วย
user253751

1
@immibis ได้เพิ่มเลเยอร์ที่ผู้ใช้สามารถจัดการได้โดยไม่ต้องจัดการส่วนที่เหลือของฐานข้อมูลหรือต้องการให้มีการใช้งานซ้ำเพื่ออัปเดตโมเดล

1
@immibis EAV ได้ถกเถียงกันอย่างถึงพริกถึงขิงในฐานข้อมูลเชิงสัมพันธ์เป็นเวลาหลายปี ในทางทฤษฎีแล้วมันไม่จำเป็น แต่ในทางปฏิบัติคุณไม่สามารถทำบางสิ่งได้หากไม่มีมัน
Ross Patterson

1
@ShivanDragon ที่ไปยังแนวทาง NoSQL ที่เก็บเอกสารเพียงเก็บเอกสารและไม่กำหนดสคีมา ดังนั้นการเพิ่มและลบฟิลด์และการแยกวิเคราะห์เอกสารนั้นอยู่นอกขอบเขตของฐานข้อมูลทั้งหมด (และคุณได้เขียนแบบจำลองของคุณเพื่อรองรับนั้น) เป็นชุดของการประนีประนอมที่แตกต่างอย่างสิ้นเชิงจากฐานข้อมูลเชิงสัมพันธ์สำหรับโครงสร้าง EAV


5

การทำเช่นนี้เป็นเรื่องยาก

สำหรับแอปพลิเคชันแบบใช้ครั้งเดียวเช่นที่คุณวางแผนคุณสามารถแน่นอนเพียงเพิ่มคอลัมน์สำหรับแต่ละฟิลด์และให้ UI ที่ทำให้การกำหนดเขตข้อมูลโดยผู้ใช้ที่ไม่ผ่านการฝึกฝนปลอดภัยกว่าการให้บรรทัดคำสั่ง SQL แก่พวกเขา หรือคุณสามารถทำตามรูปแบบEntity-Attribute-Valueที่น่ากลัวซึ่งเป็นแบบคลาสสิกหากค่อนข้างน่ากลัวตอบสนองต่อปัญหาประเภทนี้ การสร้าง UI สำหรับการกำหนดเขตข้อมูล EAV มักจะมีความซับซ้อนมากกว่าคอลัมน์ฐานข้อมูลและข้อความค้นหาอาจมีขนดกค่อนข้าง แต่สำหรับเขตข้อมูลจำนวนมาก ( เช่น schemata ที่มีความเบาบางมาก) อาจเป็นวิธีเดียวที่จะได้ งานทำ


โดยสรุป: โครงการเล็ก ๆ == KISS เปรียวลงกับพื้น
Encaitar

ปัญหาเกี่ยวกับการอัพเดตตารางฐานข้อมูลคือขึ้นอยู่กับปริมาณข้อมูลและดัชนีที่ต้องการ (ฟิลด์ที่กำหนดเองมักต้องการคุณสมบัติการค้นหา) ตารางการแก้ไขแบบสอบถามอาจใช้เวลานานมาก เรื่องสั้นที่สั้นคือ MySQL และฐานข้อมูลเชิงสัมพันธ์อื่น ๆ นั้นไม่ใช่สื่อที่ดีสำหรับความต้องการประเภทนี้
Oddman

0

ฉันมาข้ามสิ่งที่คล้ายกันเมื่อเร็ว ๆ นี้

ฉันทำ 2 ตาราง

1: table Objects 
    Id , name, type

เขาเป็นวัตถุทั้งหมดของคุณ คุณตั้งชื่อมัน

และประเภทของวัตถุนี้: - สำหรับฉันประเภทที่มีอยู่คือสินค้าคงคลัง inventory_item สำนักงาน

และการตั้งค่าปกติคือรายการ n คือรายการย่อยหรือรายการสินค้าซึ่งเป็นรายการย่อยของสำนักงานและฉันใช้ตารางเข้าร่วมเพื่อรวมวัตถุเข้าด้วยกัน

2 table settings 
     organization_Id , title, value , type

ตารางการตั้งค่ามีชื่อทุกฟิลด์สำหรับประเภทวัตถุเฉพาะนั้นและค่าเป็นค่า

ตัวอย่างคุณสมบัติของสำนักงาน

ที่ตั้ง, โทรศัพท์, เวลาทำงาน

และสำหรับรายการต่างๆ

  • จำนวน
  • ราคา
  • บาร์โค้ด

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

ดังนั้นเมื่อใดก็ตามที่ฉันต้องการสำนักงานฉันโหลดได้ง่ายด้วยความสัมพันธ์และการตั้งค่าทั้งหมดที่การตั้งค่า object_I'd (วัตถุที่ร้องขอ)

หลังจากนั้นฉันหมุนทุกแถวจากการตั้งค่าและนั่นคือ

และในกรณีที่ฉันต้องการตั้งค่าเฉพาะสำหรับรายการในสินค้าคงคลัง (ไม่ใช่ทั่วโลก) ฉันตั้งค่า object_I'd = ฉันได้จากตารางความสัมพันธ์ object_objects และฉันตั้งค่า settings.type = relationship_setting

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


2
เคล็ดลับ Pro - อย่าโพสต์ในฟอรัมนี้จากโทรศัพท์ของคุณ การแก้ไขอัตโนมัติทำให้บางส่วนของโพสต์ไม่สามารถอ่านได้
BobDalgleish

ฮ่าฮ่าสังเกตดี :)
Zalaboza

0

มันเป็นการปฏิบัติที่ไม่ถูกต้องหรือไม่ที่จะอนุญาตให้ผู้ใช้กำหนดเขตข้อมูล?

ไม่มันไม่ใช่การฝึกฝนที่ไม่ดี เป็นเรื่องธรรมดา ในแง่ของ OO สิ่งนี้เรียกว่าการสืบทอด คุณมีสินค้าคงคลังระดับฐานรายการและคลาส AudioCD และเฟอร์นิเจอร์ที่สืบทอดสองคลาส

โดยทั่วไปแล้วผู้คนจะจัดการกับสิ่งนี้ใน "ชีวิตจริง" ได้อย่างไร?

คุณต้องตัดสินใจว่าจะเก็บสินค้า Item, AudioCD และเฟอร์นิเจอร์ไว้ในฐานข้อมูลอย่างไร

หากการสืบค้นแบบง่ายมีความสำคัญที่สุดสำหรับคุณและ db-space / normalization ไม่สำคัญว่าคุณจะต้องใช้สคีมา "ตารางต่อลำดับชั้น"

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

ดูรายละเอียดเพิ่มเติมdotnet ตารางต่อชนิด VS-ตารางต่อลำดับชั้นมรดก หรือJava จำศีลมรดก


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