ฉันเพิ่งได้รับมรดกฐานข้อมูล SQL Server ที่ใช้BINARY(16)
แทนUNIQUEIDENTIFIER
การจัดเก็บ Guids มันทำสิ่งนี้สำหรับทุกสิ่งรวมถึงกุญแจหลัก
ฉันควรจะกังวลหรือไม่
ฉันเพิ่งได้รับมรดกฐานข้อมูล SQL Server ที่ใช้BINARY(16)
แทนUNIQUEIDENTIFIER
การจัดเก็บ Guids มันทำสิ่งนี้สำหรับทุกสิ่งรวมถึงกุญแจหลัก
ฉันควรจะกังวลหรือไม่
คำตอบ:
ฉันควรจะกังวลหรือไม่
มีบางสิ่งที่นี่ที่เกี่ยวข้องเล็กน้อย
ครั้งแรก:ในขณะที่มันเป็นความจริงที่UNIQUEIDENTIFIER
(เช่นGuid
) เป็นค่าไบนารี 16 ไบต์ก็เป็นความจริงที่:
INT
อาจจะเก็บไว้ในBINARY(4)
, DATETIME
สามารถเก็บไว้ในBINARY(8)
ฯลฯ ) จึง # 2 ↴sysname
เป็นชื่อแทนNVARCHAR(128)
)ความแตกต่างของพฤติกรรมสามอย่างที่ฉันสามารถหาได้คือ:
การเปรียบเทียบUNIQUEIDENTIFIER
ค่าใน SQL Server สำหรับดีกว่าหรือแย่กว่านั้นไม่ได้ทำแบบเดียวกับการเปรียบเทียบBINARY(16)
ค่า ตามหน้า MSDN สำหรับการเปรียบเทียบ GUID และค่าตัวระบุที่ไม่ซ้ำกันเมื่อเปรียบเทียบUNIQUEIDENTIFIER
ค่าใน SQL Server:
หกไบต์สุดท้ายของค่ามีความสำคัญมากที่สุด
แม้ว่าค่าเหล่านี้จะไม่ถูกจัดเรียงบ่อย ๆ แต่มีความแตกต่างเล็กน้อยระหว่างสองประเภทนี้ ตามหน้า MSDN สำหรับเครื่องระบุเอกลักษณ์ :
การสั่งซื้อไม่ได้ดำเนินการโดยการเปรียบเทียบรูปแบบบิตของค่าทั้งสอง
เนื่องจากมีความแตกต่างในวิธีจัดการค่า GUID ระหว่าง SQL Server และ. NET (บันทึกไว้ในหน้า "การเปรียบเทียบ GUID และค่า Uniqueidentifier" ที่ลิงก์ด้านบน) การดึงข้อมูลนี้ออกจาก SQL Server ลงในรหัสแอปอาจไม่ได้รับการจัดการอย่างเหมาะสม รหัสแอพหากจำเป็นต้องจำลองพฤติกรรมการเปรียบเทียบ SQL Server พฤติกรรมนั้นสามารถเลียนแบบได้ด้วยการแปลงเป็น a SqlGuid
แต่ผู้พัฒนาจะรู้หรือไม่
ที่สอง:ขึ้นอยู่กับคำสั่งดังต่อไปนี้
มันทำสิ่งนี้สำหรับทุกสิ่งรวมถึงกุญแจหลัก
ฉันจะกังวลโดยทั่วไปสำหรับประสิทธิภาพของระบบโดยใช้ GUID เป็น PK แทนที่จะเป็น Alternate Keys พร้อมกับการใช้INT
หรือแม้กระทั่งBIGINT
เป็น PK และยิ่งเป็นห่วงว่า GUID PKs เหล่านี้จะเป็นดัชนีแบบกลุ่มหรือไม่
ความคิดเห็นต่อไปนี้จัดทำโดย OP สำหรับคำตอบของ @ Rob นำมาซึ่งความกังวลเพิ่มเติม:
มันอพยพมาจากฉันคิดว่า MySQL
guid ของสามารถเก็บไว้ใน2 รูปแบบไบนารีที่แตกต่างกัน ดังนั้นอาจมีสาเหตุของความกังวลขึ้นอยู่กับ:
ปัญหาเกี่ยวกับตำแหน่งที่สร้างการแทนแบบไบนารีเกี่ยวข้องกับการเรียงลำดับไบต์ของ 3 รายการแรกจาก 4 "ฟิลด์" หากคุณไปที่ลิงก์ด้านบนไปยังบทความ Wikipedia คุณจะเห็นว่า RFC 4122 ระบุให้ใช้การเข้ารหัส "Big Endian" สำหรับทั้ง 4 ฟิลด์ แต่ Microsoft GUID นั้นระบุโดยใช้ Endianness "Native" สถาปัตยกรรมของ Intel คือ Little Endian ดังนั้นคำสั่งไบต์สำหรับ 3 ฟิลด์แรกจะกลับด้านจากระบบที่ตามหลัง RFC (รวมถึง GUID แบบ Microsoft ที่สร้างบนระบบ Big Endian) เขตข้อมูลแรก "Data 1" คือ 4 ไบต์ หนึ่งใน endianness มันจะแสดงเป็น 0x01020304
(สมมุติฐาน) แต่ใน endianness อื่น ๆ 0x04030201
มันจะเป็น ดังนั้นหากฐานข้อมูลปัจจุบัน 'BINARY(16)
การเป็นตัวแทนไบนารีนั้นถูกสร้างขึ้นบนระบบที่ทำตาม RFC จากนั้นแปลงข้อมูลในBINARY(16)
ฟิลด์เป็นวิลUNIQUEIDENTIFIER
จะส่งผลให้เกิด GUID ที่แตกต่างจากที่สร้างขึ้นในตอนแรก สิ่งนี้ไม่ได้ก่อให้เกิดปัญหาหากค่าไม่เคยออกจากฐานข้อมูลและค่าจะถูกเปรียบเทียบเพื่อความเท่าเทียมกันเท่านั้นและไม่ได้เรียงลำดับ
UNIQUEIDENTIFIER
ความกังวลที่มีการสั่งซื้อก็คือว่าพวกเขาจะไม่ได้อยู่ในลำดับเดียวกันหลังจากแปลงไป โชคดีถ้าระบบเดิมจริงๆคือ MySQL แล้วการสั่งซื้อก็ไม่เคยทำบนฐานเป็นตัวแทนในสถานที่แรกตั้งแต่ MySQL เพียง แต่มีการแสดงสตริงของUUID
ความกังวลเกี่ยวกับค่าสตริงที่ใช้ภายนอกฐานข้อมูลจะรุนแรงมากขึ้นอีกครั้งหากมีการสร้างตัวแทนไบนารีนอก Windows / SQL Server เนื่องจากการเรียงลำดับไบต์อาจแตกต่างกันดังนั้น GUID เดียวกันในรูปแบบสตริงจะส่งผลให้มีการแทนค่าไบนารีแบบต่างกัน 2 แบบขึ้นอยู่กับตำแหน่งที่เกิดการแปลง หากรหัสแอปหรือลูกค้าได้รับ GUID ในรูปแบบสตริงว่าABC
มาจากรูปแบบไบนารีของ123
และการเป็นตัวแทนไบนารีถูกสร้างขึ้นบนระบบต่อ RFC ดังนั้นการเป็นตัวแทนไบนารีเดียวกัน (เช่น123
) จะแปลเป็นรูปแบบสตริงของDEF
เมื่อถูกแปลงเป็น กUNIQUEIDENTIFIER
. ในทำนองเดียวกันในรูปแบบเดิมของสตริงABC
จะแปลงเป็นรูปแบบไบนารีของเมื่อแปลงเป็น456
UNIQUEIDENTIFIER
ดังนั้นหาก GUID ไม่เคยออกจากฐานข้อมูลก็ไม่จำเป็นต้องกังวลเกี่ยวกับการสั่งซื้อ หรือถ้าการนำเข้าจาก MySQL ทำได้โดยการแปลงรูปแบบสตริง (เช่นFCCEC3D8-22A0-4C8A-BF35-EC18227C9F40
) มันอาจจะโอเค มิฉะนั้นหากมีการมอบ GUID เหล่านั้นให้กับลูกค้าหรือในรหัสแอปคุณสามารถทดสอบเพื่อดูว่าพวกเขาแปลงได้อย่างไรโดยรับมาแปลงผ่านSELECT CONVERT(UNIQUEIDENTIFIER, 'value found outside of the database');
และดูว่าคุณพบระเบียนที่คาดหวังหรือไม่ BINARY(16)
ถ้าคุณไม่สามารถตรงกับบันทึกแล้วคุณอาจจะต้องเก็บเป็นทุ่งนา
ในทุกโอกาสที่จะไม่มีปัญหา แต่ฉันพูดถึงเรื่องนี้เพราะภายใต้เงื่อนไขที่เหมาะสมอาจมีปัญหา
และ GUID ใหม่จะถูกแทรกอย่างไร? สร้างขึ้นในรหัสแอพไหม
หากคำอธิบายก่อนหน้าของปัญหาที่อาจเกิดขึ้นที่เกี่ยวข้องกับการนำเข้าการเป็นตัวแทนไบนารีของ GUID ที่สร้างขึ้นในระบบอื่นนั้นทำให้สับสนเล็กน้อย (หรือมาก) หวังว่าสิ่งต่อไปนี้จะชัดเจนขึ้นเล็กน้อย:
DECLARE @GUID UNIQUEIDENTIFIER = NEWID();
SELECT @GUID AS [String], CONVERT(BINARY(16), @GUID) AS [Binary];
-- String = 5FED23BE-E52C-40EE-8F45-49664C9472FD
-- Binary = 0xBE23ED5F2CE5EE408F4549664C9472FD
-- BE23ED5F-2CE5-EE40-8F45-49664C9472FD
ในผลลัพธ์ที่แสดงด้านบนค่า "String" และ "Binary" มาจาก GUID เดียวกัน ค่าที่อยู่ใต้บรรทัด "Binary" เป็นค่าเดียวกันกับบรรทัด "Binary" แต่จัดรูปแบบในลักษณะเดียวกับบรรทัด "String" (เช่นลบ "0x" และเพิ่มขีดกลางสี่เส้น) เมื่อเปรียบเทียบกับค่าที่หนึ่งและที่สามพวกเขาจะไม่เหมือนกัน แต่ใกล้เคียงกันมาก: ส่วนขวาสุดทั้งสองเหมือนกัน แต่ส่วนที่เหลือทั้งสามนั้นไม่เหมือนกัน แต่ถ้าคุณดูอย่างใกล้ชิดคุณจะเห็นว่ามันเป็นไบต์เดียวกันในแต่ละส่วนทั้งสามโดยเรียงตามลำดับที่แตกต่างกัน มันอาจจะง่ายกว่าที่จะดูว่าฉันแสดงเฉพาะสามส่วนแรกเหล่านั้นและจำนวนไบต์ดังนั้นมันจะง่ายกว่าที่จะเห็นว่าลำดับของพวกเขาแตกต่างกันอย่างไรระหว่างการรับรองทั้งสอง:
String = 1 5F 2 ED 3 23 4 BE - 5 E5 6 2C - 7 40 8 EE
Binary = 4 BE 3 23 2 ED 1 5F - 6 2C 5 E5 - 8 EE 7 40 (ใน Windows / SQL Server)
ดังนั้นภายในแต่ละกลุ่มการเรียงลำดับของไบต์จะถูกย้อนกลับ แต่เฉพาะใน Windows และ SQL Server อย่างไรก็ตามในระบบที่ติดกับ RFC การแทนแบบไบนารี่จะทำมิเรอร์การแทนแบบ sting เนื่องจากจะไม่มีการกลับรายการของคำสั่งไบต์ใด ๆ
ข้อมูลถูกนำเข้าสู่ SQL Server จาก MySQL อย่างไร ที่นี่มีตัวเลือกน้อย:
SELECT CONVERT(BINARY(16), '5FED23BE-E52C-40EE-8F45-49664C9472FD'),
CONVERT(BINARY(16), 0x5FED23BEE52C40EE8F4549664C9472FD),
CONVERT(BINARY(16), CONVERT(UNIQUEIDENTIFIER, '5FED23BE-E52C-40EE-8F45-49664C9472FD'));
ผลตอบแทน:
0x35464544323342452D453532432D3430
0x5FED23BEE52C40EE8F4549664C9472FD
0xBE23ED5F2CE5EE408F4549664C9472FD
สมมติว่ามันเป็นไบนารี่ถึงไบนารี่แบบตรง (เช่นแปลง # 2 ด้านบน) ดังนั้นผลลัพธ์ของ GUID หากแปลงเป็นจริงUNIQUEIDENTIFIER
จะเป็น:
SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);
ผลตอบแทน:
BE23ED5F-2CE5-EE40-8F45-49664C9472FD
ซึ่งเป็นสิ่งที่ผิด และนั่นทำให้เรามีคำถามสามข้อ:
คุณสามารถเป็นห่วงได้เสมอ ;)
ระบบอาจได้รับการโยกย้ายจากระบบอื่นที่ไม่สนับสนุนตัวระบุเฉพาะ มีการประนีประนอมอื่น ๆ ที่คุณไม่รู้หรือไม่?
ผู้ออกแบบอาจไม่ทราบเกี่ยวกับประเภทตัวระบุเฉพาะ พวกเขาไม่รู้อะไรอีกแล้ว
แม้ว่าในทางเทคนิคแล้ว - มันไม่ควรเป็นปัญหาสำคัญ