การจัดเก็บรายการที่มีการคั่นในคอลัมน์ฐานข้อมูลนั้นแย่มากจริงหรือ?


363

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

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

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

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


21
ในกรณีนี้ทำไมฐานข้อมูลรบกวนการบันทึกไฟล์จะทำ
thavan

6
เห็นด้วยกับ @thavan ทำไมถึงต้องบันทึกข้อมูลเพื่อพิสูจน์แนวคิด? เมื่อคุณได้พิสูจน์เสร็จสมบูรณ์แล้วเพิ่มฐานข้อมูลอย่างถูกต้อง น้ำหนักเบาในการทำของคุณเพื่อพิสูจน์แนวคิดไม่ต้องทำสิ่งที่คุณต้องเลิกทำในภายหลัง
Jeff Davis

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

คำตอบ:


568

นอกเหนือจากการละเมิดฟอร์มปกติแรกเนื่องจากกลุ่มค่าที่ซ้ำกันที่จัดเก็บไว้ในคอลัมน์เดียวรายการที่คั่นด้วยเครื่องหมายจุลภาคมีปัญหาเชิงปฏิบัติอื่น ๆ อีกมากมาย:

  • ไม่สามารถมั่นใจได้ว่าแต่ละค่าเป็นประเภทข้อมูลที่ถูกต้อง: ไม่มีวิธีป้องกัน1,2,3 กล้วย 5
  • ไม่สามารถใช้ข้อ จำกัด กุญแจต่างประเทศเพื่อเชื่อมโยงค่ากับตารางการค้นหา; ไม่มีวิธีในการบังคับใช้ Referential Integrity
  • ไม่สามารถบังคับใช้เอกลักษณ์ได้: ไม่มีวิธีในการป้องกัน1,2,3,3,3,5
  • ไม่สามารถลบค่าออกจากรายการโดยไม่ดึงข้อมูลทั้งรายการ
  • ไม่สามารถจัดเก็บรายการนานกว่าที่เหมาะสมในคอลัมน์สตริง
  • ยากที่จะค้นหาเอนทิตีทั้งหมดที่มีค่าที่กำหนดในรายการ คุณต้องใช้การสแกนตารางที่ไม่มีประสิทธิภาพ อาจต้องหันไปใช้นิพจน์ทั่วไปตัวอย่างเช่นใน MySQL:
    idlist REGEXP '[[:<:]]2[[:>:]]'*
  • องค์ประกอบที่นับได้ยากในรายการหรือทำแบบสอบถามแบบรวมอื่น ๆ
  • ยากที่จะรวมค่ากับตารางการค้นหาที่อ้างอิง
  • ยากที่จะดึงรายการในลำดับที่เรียง

เพื่อแก้ปัญหาเหล่านี้คุณต้องเขียนตันของรหัสโปรแกรม, การปฏิรูปการทำงานว่า RDBMS แล้วให้มีประสิทธิภาพมากขึ้น

รายการคั่นด้วยเครื่องหมายจุลภาคจะเพียงพอที่ผิดที่ฉันทำนี้บทแรกในหนังสือของฉัน: SQL Antipatterns: หลีกเลี่ยงการผิดพลาดของการเขียนโปรแกรมฐานข้อมูล

มีหลายครั้งที่คุณจำเป็นต้องใช้ denormalization แต่เมื่อ@OMG Ponies กล่าวถึงกรณีเหล่านี้เป็นข้อยกเว้น "การเพิ่มประสิทธิภาพ" ที่ไม่ใช่เชิงสัมพันธ์จะได้รับประโยชน์จากการสืบค้นหนึ่งประเภทโดยเสียค่าใช้จ่ายจากการใช้ข้อมูลอื่นดังนั้นโปรดตรวจสอบให้แน่ใจว่าคุณทราบว่าการสืบค้นใดที่จำเป็นต้องได้รับการปฏิบัติเป็นพิเศษ


* MySQL 8.0 ไม่รองรับไวยากรณ์นิพจน์ขอบเขตนี้อีกต่อไป


8
ARRAY (ของประเภทข้อมูลใด ๆ ) สามารถแก้ไขข้อยกเว้นเพียงตรวจสอบ PostgreSQL: postgresql.org/docs/current/static/arrays.html (@Bill: หนังสือที่ยอดเยี่ยมต้องอ่านสำหรับนักพัฒนาหรือ dba)
Frank Heikens

4
+1 Bill Karwin คำตอบยอดเยี่ยม! จุดกระสุนที่น่ารักกระชับ มันดูเหมือนหนังสือที่ยอดเยี่ยมเช่นกัน รักปกด้วย +1 NullUserException ฉันกำลังออกแบบสคีมาสำหรับฐานข้อมูล MySQL เพื่อแทนที่ระบบไฟล์แบบข้อความ ฉันได้พบกับวิกฤติหลายครั้งแล้ว ดังนั้นหนังสือเล่มนี้จะคุ้มค่ากับการซื้อ
therobyouknow

2
เว็บไซต์ pragprog.com ก็ดูดีเช่นกัน: รูปแบบที่ดีรูปแบบสะอาดตาเป็นมิตรกับผู้ใช้ นี่จะค่อนข้างใหม่ฉันไม่สามารถซื้อ ebooks ได้ในอดีต PS ฉันไม่ทำงานสำหรับพวกเขามีการเชื่อมต่อกับผู้เขียน ฉันชอบที่จะเฉลิมฉลองผลิตภัณฑ์บริการและความช่วยเหลือที่ดีเมื่อฉันเห็นมัน
therobyouknow

2
ในด้านที่จริงจังฉันจะเพิ่มในรายการของคุณ: ค้นหายาก สมมติว่าคุณต้องการบันทึกทั้งหมดที่มี "2" แน่นอนคุณไม่สามารถค้นหา foobar = '2' ได้เพราะนั่นจะทำให้พลาดหากมีค่าอื่น ๆ คุณไม่สามารถค้นหาคำว่า foobar เช่น '% 2%' ได้เพราะจะได้รับเพลงที่ผิดสำหรับ 12 และ 28 เป็นต้นไป คุณไม่สามารถค้นหา foobar เช่น '%, 2,%' เนื่องจาก 2 อาจเป็นองค์ประกอบแรกหรือสุดท้ายของรายการและมีเครื่องหมายจุลภาคหนึ่งรายการ
Jay

2
ฉันรู้ว่ามันไม่แนะนำ แต่ผู้เล่นที่สนับสนุนปีศาจ: สิ่งเหล่านี้ส่วนใหญ่สามารถถอดออกได้หากมี UI ที่จัดการเอกลักษณ์และชนิดข้อมูล (ไม่เช่นนั้นจะเกิดข้อผิดพลาดหรือทำงานผิดปกติ) UI ก็ลดลงและสร้างมันต่อไป ค่ามาจากการทำให้พวกเขาไม่ซ้ำกันฟิลด์เช่น '% P%' สามารถใช้ค่าเป็น P, R, S, T, การนับไม่สำคัญและการเรียงลำดับไม่สำคัญ ขึ้นอยู่กับ UI ค่าสามารถแยก [] เช่นเพื่อทำเครื่องหมายในกล่องกาเครื่องหมายในรายการจากตารางโปรแกรมควบคุมในสถานการณ์ทั่วไปอย่างน้อยโดยไม่ต้องไปที่ตารางอื่นเพื่อรับค่า
jmcclure

44

"เหตุผลหนึ่งคือความเกียจคร้าน"

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

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

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


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

41

มีคำถามมากมายเกี่ยวกับการถามดังนั้น:

  • วิธีรับจำนวนค่าเฉพาะจากรายการที่คั่นด้วยเครื่องหมายจุลภาค
  • วิธีรับเร็กคอร์ดที่มีค่าเฉพาะ 2/3 / etc เฉพาะจากรายการที่คั่นด้วยเครื่องหมายจุลภาค

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

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


19

โดยทั่วไปสิ่งใดที่สามารถป้องกันได้หากเป็นไปตามข้อกำหนดของโครงการของคุณ นี่ไม่ได้หมายความว่าผู้คนจะเห็นด้วยหรือต้องการปกป้องการตัดสินใจของคุณ ...

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


10

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

มันแบ่งรูปแบบปกติครั้งแรก

คำติชมที่สองคือการใส่ผลลัพธ์ดิบโดยตรงลงในฐานข้อมูลโดยไม่มีการตรวจสอบหรือมีผลผูกพันใด ๆ ทำให้คุณเปิดการโจมตี SQL injection

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

หรือปล่อยให้มันเป็นและเรียนรู้บทเรียนที่เจ็บปวดของการโจมตีฉีด SQL


19
ฉันไม่เห็นอะไรเลยในคำถามนี้ที่แนะนำว่าเขาเสี่ยงต่อการฉีด SQL การฉีด SQL และการนอร์มัลไลซ์ฐานข้อมูลเป็นหัวข้อมุมฉากและการพูดนอกเรื่องของคุณเกี่ยวกับการฉีดนั้นไม่เกี่ยวข้องกับคำถาม
Hammerite

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

1
@Hammerite - การคาดคะเนของคุณต่อรถเมล์นั้นไร้สาระ
duffymo

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

1
ใช่ฉันเห็น. ฉันคิดว่าฉันมีเหตุผลมากขึ้นที่เตือนคุณเกี่ยวกับรถโดยสาร
duffymo

7

ฉันใช้รายการแยกคู่คีย์ / ค่าในคอลัมน์ NTEXT ใน SQL Server มานานกว่า 4 ปีแล้วและใช้งานได้ คุณสูญเสียความยืดหยุ่นในการทำเคียวรี แต่ในทางกลับกันหากคุณมีห้องสมุดที่ยังคงมีอยู่ / จับคู่คู่ค่าคีย์ไม่เป็นความคิดที่ไม่ดี


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

5
พอลฉันเห็นด้วย แต่อย่างที่ฉันบอกว่าฉันใช้เพื่อวัตถุประสงค์เฉพาะและนั่นเป็นการดำเนินการป้อนข้อมูลที่คุณมีหลายรูปแบบ ฉันกำลังทบทวนการออกแบบตอนนี้ฉันได้เรียนรู้ NHibernate แต่กลับมาแล้วฉันต้องการความยืดหยุ่นในการออกแบบฟอร์มใน ASP.NET และใช้รหัสข้อความเป็นกุญแจสำคัญในคู่ของคีย์ / ค่า
ราชา

28
+1 เพียงเพื่อตอบโต้ downvotes การบอกคนที่ดูแลแอพนี้เป็นเวลา 4 ปีเกี่ยวกับความกังวลในการบำรุงรักษานั้นค่อนข้างเกรงใจ มีแนวคิด "น่ากลัว" น้อยมากในการพัฒนา sw - ส่วนใหญ่เป็นเพียงแนวคิดที่มีการบังคับใช้ที่ จำกัด มาก มีเหตุผลที่จะเตือนผู้คนถึงข้อ จำกัด แต่การตีสอนผู้ที่เคยทำและดำเนินชีวิตผ่านมันทำให้ข้าเป็นท่าทีที่ดีกว่าที่ข้าทำได้
Mark Brackett

7

ฉันต้องการคอลัมน์หลายค่าสามารถนำไปใช้เป็นฟิลด์ xml ได้

สามารถแปลงเป็นตัวคั่นจุลภาคได้ตามความจำเป็น

การสืบค้นรายการ XML ในเซิร์ฟเวอร์ sql โดยใช้ XqueryXquery

ด้วยการเป็นฟิลด์ xml ข้อกังวลบางประการสามารถแก้ไขได้

ด้วย CSV:ไม่สามารถมั่นใจได้ว่าแต่ละค่าเป็นประเภทข้อมูลที่ถูกต้อง: ไม่มีวิธีป้องกัน 1,2,3 กล้วย 5

ด้วย XML:ค่าในแท็กสามารถบังคับให้เป็นประเภทที่ถูกต้องได้


ด้วย CSV:ไม่สามารถใช้ข้อ จำกัด foreign key เพื่อเชื่อมโยงค่ากับตารางการค้นหา; ไม่มีวิธีในการบังคับใช้ Referential Integrity

ด้วย XML:ยังคงมีปัญหา


ด้วย CSV:ไม่สามารถบังคับใช้เอกลักษณ์ได้: ไม่มีวิธีป้องกัน 1,2,3,3,3,5

ด้วย XML:ยังคงมีปัญหา


ด้วย CSV:ไม่สามารถลบค่าออกจากรายการโดยไม่ต้องดึงข้อมูลทั้งรายการ

ด้วย XML:สามารถลบรายการเดียวได้


ด้วย CSV:ยากที่จะค้นหาเอนทิตีทั้งหมดที่มีค่าที่กำหนดในรายการ คุณต้องใช้การสแกนตารางที่ไม่มีประสิทธิภาพ

ด้วยฟิลด์XML: xml สามารถทำดัชนีได้


ด้วย CSV:องค์ประกอบที่นับได้ยากในรายการหรือทำแบบสอบถามแบบรวมอื่น ๆ **

ด้วย XML:ไม่ยากโดยเฉพาะ


ด้วย CSV:ยากที่จะเข้าร่วมค่ากับตารางการค้นหาที่พวกเขาอ้างอิง **

ด้วย XML:ไม่ยากโดยเฉพาะ


ด้วย CSV:ยากที่จะดึงข้อมูลรายการในลำดับที่เรียง

ด้วย XML:ไม่ยากโดยเฉพาะ


ด้วย CSV: การจัดเก็บจำนวนเต็มเป็นสตริงจะใช้พื้นที่ประมาณสองเท่าของการจัดเก็บจำนวนเต็มแบบไบนารี

ด้วย XML:พื้นที่จัดเก็บยิ่งกว่า csv


ด้วย CSV:มีอักขระจุลภาคจำนวนมาก

ด้วย XML: ใช้แท็กแทนเครื่องหมายจุลภาค


กล่าวโดยย่อคือการใช้ XML ทำให้เกิดปัญหากับรายการที่มีการคั่นและสามารถแปลงเป็นรายการที่มีการคั่นได้ตามต้องการ


6

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


0

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


แบบฟอร์มมีเขตข้อมูลเพิ่มเติมบางส่วนนี่เป็นเพียงส่วนหนึ่งของแบบฟอร์ม (ซึ่งฉันไม่ได้อธิบายได้ดีในคำถาม)
นักวิทยาศาสตร์บ้า

0

หากคุณมีฟิลด์บูลีนจำนวนคงที่คุณสามารถใช้INT(1) NOT NULL(หรือBIT NOT NULLถ้ามี) หรือCHAR (0)(เป็นโมฆะ) สำหรับแต่ละฟิลด์ คุณสามารถใช้SET(ฉันลืมไวยากรณ์ที่แน่นอน)


1
INT(1)ใช้เวลา 4 ไบต์ ที่(1)ไม่มีความหมาย
Rick James
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.