ใช้ตารางการกำหนดค่าแถวเดียวในฐานข้อมูล SQL Server ความคิดที่ไม่ดี?


145

ในการพัฒนาแอปพลิเคชั่นตะกร้าสินค้าฉันพบว่าฉันต้องการบันทึกการตั้งค่าและการกำหนดค่าตามความต้องการและความต้องการของผู้ดูแลระบบ ข้อมูลนี้สามารถเป็นอะไรก็ได้จากข้อมูล บริษัท รหัสบัญชีการจัดส่งคีย์ PayPal API การตั้งค่าการแจ้งเตือน ฯลฯ

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

วิธีที่เหมาะสมในการจัดเก็บข้อมูลนี้คืออะไร?

หมายเหตุ: DBMS ของฉันคือ SQL Server 2008 และมีการใช้งานเลเยอร์การเขียนโปรแกรมด้วย ASP.NET (ใน C #)

คำตอบ:


189

ฉันได้ทำสองวิธีนี้ในอดีต - ตารางแถวเดี่ยวและตารางคู่คีย์ / ค่า - และมีข้อดีและข้อเสียสำหรับแต่ละวิธี

แถวเดียว

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

คู่ของคีย์ / ค่า

  • บวก: การเพิ่มการตั้งค่าใหม่ไม่จำเป็นต้องมีการเปลี่ยนแปลงสคีมา
  • positive: schema ของตารางแคบด้วยการใช้แถวพิเศษสำหรับการตั้งค่าใหม่
  • เป็นค่าลบ: การตั้งค่าแต่ละรายการมีค่าเริ่มต้นเหมือนกัน (ว่างเปล่า / ว่างเปล่า)
  • ค่าลบ: ทุกอย่างจะต้องเก็บไว้เป็นสตริง (เช่น. nvarchar)
  • ค่าลบ: เมื่อจัดการกับการตั้งค่าในโค้ดคุณต้องทราบว่าการตั้งค่าประเภทใดและใช้งานอะไร

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

สิ่งหนึ่งที่ฉันกังวลเกี่ยวกับการใช้วิธีนี้คือการมีหลายแถวในตารางการตั้งค่าแถวเดียว "พิเศษ" ฉันเอาชนะสิ่งนี้โดย (ใน SQL Server):

  • การเพิ่มคอลัมน์บิตใหม่ด้วยค่าเริ่มต้นที่ 0
  • สร้างข้อ จำกัด การตรวจสอบเพื่อให้แน่ใจว่าคอลัมน์นี้มีค่า 0
  • การสร้างข้อ จำกัด ที่ไม่ซ้ำใครในคอลัมน์บิต

ซึ่งหมายความว่ามีเพียงหนึ่งแถวเท่านั้นที่สามารถมีอยู่ในตารางได้เนื่องจากคอลัมน์บิตต้องมีค่า 0 แต่สามารถมีได้เพียงหนึ่งแถวที่มีค่านั้นเนื่องจากข้อ จำกัด ที่ไม่ซ้ำกัน


5
เราทำสิ่งเดียวในแอปพลิเคชัน LOB ของเรา ค่าเป็นประเภทที่ถูกต้องทั้งหมดซึ่งทำให้การใช้งานในแอปพลิเคชันง่ายขึ้นมาก สคีมาของเรานั้นเป็นรุ่นพร้อมกับแอปพลิเคชันดังนั้นการเปลี่ยนแปลงการตั้งค่าการกำหนดค่าจะได้รับการจัดการเหมือนกับการแก้ไขแอปพลิเคชันอื่น ๆ
DaveE

17
ค่าบวกแถวเดียว: สามารถกำหนด FK ในบางคอลัมน์ได้!
wqw

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

19
สิ่งหนึ่งที่สามารถทำลายวันของคุณจริง ๆ หลังจากใช้งานโซลูชันแถวเดียวคือเมื่อคุณได้รับมอบหมายในภายหลังด้วย "มาติดตามเวลาครั้งสุดท้ายที่แต่ละค่าเปลี่ยนไปและใครเปลี่ยนมัน .... "
Dave Mateer

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

10

คุณควรสร้างตารางที่มีคอลัมน์สำหรับประเภทข้อมูลและค่าข้อมูล (อย่างน้อย) วิธีนี้คุณหลีกเลี่ยงไม่ต้องสร้างคอลัมน์ใหม่ทุกครั้งที่มีการเพิ่มข้อมูลใหม่


1
เรียบง่ายและเรียบร้อย ทำงานกับรายการคู่ค่าคีย์จากที่นั่น คุณอาจต้องการคิดเกี่ยวกับค่าเริ่มต้นเล็กน้อยขึ้นอยู่กับบริบทของการใช้ ...
Paul Kohler

4
ทำไมจึงมีปัญหาในการสร้างคอลัมน์ใหม่ ฉันรู้ว่ามีสถานการณ์ที่นักพัฒนาต้องหลีกเลี่ยงเนื่องจากปัญหาทางการเมืองด้วยการอัพเดตสกีมา SQL แต่ไม่มีการพูดถึงเรื่องนี้ในคำถาม
finnw

6

แถวเดียวจะทำงานได้ดี; มันจะมีประเภทที่แข็งแกร่ง:

show_borders    bit
admin_name      varchar(50)
max_users       int

ข้อเสียอย่างหนึ่งคือมันต้องเปลี่ยน schema ( alter table) เพื่อเพิ่มการตั้งค่าใหม่ อีกทางเลือกหนึ่งคือ normalizing ที่คุณท้ายด้วยตารางเช่น:

pref_name       varchar(50) primary key
pref_value      varchar(50) 

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


4

ส่วนตัวฉันจะเก็บไว้ในแถวเดียวถ้านั่นคือสิ่งที่ทำงาน เกินขนาดที่จะเก็บไว้ในตาราง SQL? อาจ แต่ไม่มีอันตรายจริงในการทำเช่นนั้น


4

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

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

หากคุณต้องการที่จะอยู่ใกล้ชิดกับรูปแบบความสัมพันธ์ที่รุ่น Entity-Attribute-Valueน่าจะเป็นสิ่งที่คุณต้องการด้วยเหตุนี้แต่ละค่าจะถูกเก็บไว้ในตารางที่มักจะมีลักษณะเช่น:

EntityId     (foreign key to the "owner" of this attribute)
AttributeId  (foreign key to the "metadata" table where the attribute is defined)
StringValue  (it is often convenient to have different columns of different types
IntValue      allowing to store the various attributes in a format that befits 
              them)

เหตุใด AttributeId คือ foreign key ไปยังตารางที่มีการกำหนด Attribute ("พารามิเตอร์การกำหนดค่า" ในกรณีของคุณ) แต่ละรายการพร้อมระบุ

AttributeId  (Primary Key)
Name
AttributeType     (some code  S = string, I = Int etc.)
Required          (some boolean indicating that this is required)
Some_other_fields   (for example to define in which order these attributes get displayed etc...)

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

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


3
ดูเหมือนว่า overkill สำหรับการใช้งานส่วนใหญ่ของตารางการกำหนดค่า
JerryOL

ฉันคิดว่าความคิดทั่วไปที่อยู่เบื้องหลังวิธีการนี้ดีมาก แต่ทำไม XML เพียงแค่เลือกรูปแบบการแลกเปลี่ยนข้อมูลอย่างง่าย ๆ เช่น JSON หรือ YAML และคุณสามารถได้รับประโยชน์จากทั้งสองรูปแบบอื่น ๆ
schlamar

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

3

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

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


2

คู่ของคีย์และค่าคล้ายกับ. Net App.Config ซึ่งสามารถจัดเก็บการตั้งค่า

ดังนั้นเมื่อคุณต้องการดึงค่าที่คุณสามารถทำได้:

SELECT value FROM configurationTable
WHERE ApplicationGroup = 'myappgroup'
AND keyDescription = 'myKey';

1

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

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

property_entry_table

[id, scope, refId, propertyName, propertyValue, propertyType] 
1, 0, 1, "COMPANY_INFO", "Acme Tools", "ADMIN"  
2, 0, 1, "SHIPPING_ID", "12333484", "ADMIN"  
3, 0, 1, "PAYPAL_KEY", "2143123412341", "ADMIN"   
4, 0, 1, "PAYPAL_KEY", "123412341234123", "ADMIN"  
5, 0, 1, "NOTIF_PREF", "ON", "ADMIN"  
6, 0, 2, "NOTIF_PREF", "OFF", "ADMIN"   

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

ในตัวอย่างนี้ขอบเขตและ refId ของคุณสามารถใช้สำหรับสิ่งที่คุณต้องการในส่วนหลัง ดังนั้นหาก propertyType "ADMIN" มีขอบเขต 0 อ้างอิง 2 คุณจะรู้ว่ามันคืออะไร

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

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

ตัวอย่างเช่น: หากคุณต้องการจัดเก็บDATABASE_VERSIONของคุณคุณจะใช้ตารางแบบนี้ ด้วยวิธีนี้เมื่อคุณต้องการอัปเกรดแอปคุณสามารถตรวจสอบตารางคุณสมบัติเพื่อดูว่าซอฟต์แวร์ของลูกค้ามีเวอร์ชันใด

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


@finnw ฉันเห็นด้วยอย่างยิ่งว่าวิธีการนี้ไม่ควรใช้กับการค้นหาโดยเฉพาะเมื่อมีการค้นหาหลายประเภท บางทีฉันอาจเข้าใจผิดคำถาม มันฟังดูเหมือนว่าเขาต้องการตารางสำหรับค่าคงที่และคุณสมบัติของระบบ ในกรณีนั้นเหตุใดจึงมี 10 ตารางที่แตกต่างกัน
Stephano

หมายเหตุ: เขาพูดว่า "บันทึกการตั้งค่าและการกำหนดค่า" ไม่ใช่ "ฉันต้องบันทึกข้อมูลรถเข็นสัมพันธ์"
Stephano

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

สัญชาตญาณแรกของฉันคือการเพิ่มข้อมูลนี้ลงในไฟล์แฟลต และคุณถูกต้องกระบวนการใช้ตารางนี้แทนจะหลีกเลี่ยงข้อ จำกัด กลไกของ DBMS อย่างไรก็ตามฉันจะบอกว่าถ้าคุณพยายามอย่างหนักเกินไปที่จะทำตามเทคนิคฐานข้อมูลที่เหมาะสมคุณจะพลาดจุด ลองดูคำตอบแรก โหวตสูงสุดใน SO: stackoverflow.com/questions/406760/…
Stephano

2
ฉันจะไปคู่ค่าคีย์ทิ้งมันทั้งหมดลงในพจนานุกรมเมื่อเริ่มต้นและเรียงลำดับของคุณ
Paul Creasey

0

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

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


0

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

ดังนั้นตารางของคุณจะมีลักษณะดังนี้:

id, column_num, property_name, intValue, floatValue, charValue, dateValue
1, 1, weeks, 51, , ,
2, 2, pi, , 3.14159, , 
3, 4, FiscYearEnd, , , , 1/31/2015
4, 3, CompanyName, , , ACME, 

มันใช้พื้นที่เพิ่มขึ้นเล็กน้อย แต่โดยส่วนใหญ่คุณใช้คุณสมบัติไม่กี่โหล คุณสามารถใช้คำสั่ง case ปิดค่า column_num เพื่อดึง / เข้าร่วมฟิลด์ที่ถูกต้อง


0

ขอโทษฉันมาเหมือนมาทีหลัง แต่อย่างไรก็ตามสิ่งที่ฉันทำนั้นเรียบง่ายและมีประสิทธิภาพ ฉันเพียงสร้างตารางที่มีสามคอลัมน์ ():

ID - int (11)

ชื่อ - varchar (64)

ค่า - ข้อความ

สิ่งที่ฉันทำก่อนที่จะสร้างคอลัมน์กำหนดค่าใหม่อัปเดตหรืออ่านคือการทำให้อนุกรมเป็น "ค่า"! วิธีนี้ฉันแน่ใจว่าของประเภท (ดี php คือ :))

ตัวอย่างเช่น

b: 0; สำหรับB OOLEAN ( false )

b: 1; สำหรับB OOLEAN ( จริง )

i: 1988; สำหรับฉัน NT

s: 5: "Kader"; ใช้สำหรับS TRING ที่มีความยาว 5 ตัวอักษร

ฉันหวังว่านี่จะช่วยได้ :)


1
ทำไมไม่สร้างคอลัมน์ใหม่สำหรับประเภทนี้ i:1988ดูเหมือนว่าคุณกำลังพยายามยุบข้อมูลสองส่วนไว้ในคอลัมน์เดียว
maksymiuk

@maksymiuk SImply เพราะเมื่อ unserialized คุณจะได้รับประเภทที่แน่นอนแทนการใช้ลูปหลังจาก (ถ้าหรือเปลี่ยน) ... ฯลฯ
Kader Bouyakoub

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

ค่าเฉลี่ยของคุณโดยทำอะไรบางอย่างเช่นecho (int) $varจำนวนเต็มและชนิดอื่น ๆ ?
Kader Bouyakoub

0

มีคอลัมน์คีย์เป็น varchar และคอลัมน์ค่าเป็น JSON 1เป็นตัวเลขในขณะที่"1"เป็นสตริง trueและfalseเป็นบูลีนทั้งคู่ คุณสามารถมีวัตถุได้เช่นกัน

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