Gần đây tôi đã kế thừa cơ sở dữ liệu SQL Server sử dụng BINARY(16)
thay vì UNIQUEIDENTIFIER
lưu trữ Hướng dẫn. Nó làm điều này cho tất cả mọi thứ bao gồm các khóa chính.
Tôi có nên quan tâm không?
Gần đây tôi đã kế thừa cơ sở dữ liệu SQL Server sử dụng BINARY(16)
thay vì UNIQUEIDENTIFIER
lưu trữ Hướng dẫn. Nó làm điều này cho tất cả mọi thứ bao gồm các khóa chính.
Tôi có nên quan tâm không?
Câu trả lời:
Tôi có nên quan tâm không?
Vâng, có một vài điều ở đây có một chút liên quan.
Đầu tiên: mặc dù đúng UNIQUEIDENTIFIER
(tức là Guid
) là giá trị nhị phân 16 byte, nhưng cũng đúng:
INT
có thể được lưu trữ trong BINARY(4)
, DATETIME
có thể được lưu trữ trong BINARY(8)
, v.v.), do đó # 2sysname
như bí danh cho NVARCHAR(128)
).Ba khác biệt về hành vi mà tôi có thể tìm thấy là:
So sánh UNIQUEIDENTIFIER
các giá trị trong SQL Server, tốt hơn hay tồi tệ hơn, thực tế không được thực hiện giống như so sánhBINARY(16)
các giá trị. Theo trang MSDN để so sánh các giá trị GUID và định danh duy nhất , khi so sánh UNIQUEIDENTIFIER
các giá trị trong SQL Server:
sáu byte cuối cùng của một giá trị là quan trọng nhất
Mặc dù các giá trị này không được sắp xếp thường xuyên, có một chút khác biệt giữa hai loại này. Theo trang MSDN cho uniqueidentifier :
thứ tự không được thực hiện bằng cách so sánh các mẫu bit của hai giá trị.
Do có sự khác biệt về cách xử lý các giá trị GUID giữa SQL Server và .NET (được lưu ý trong trang "So sánh giá trị GUID và giá trị định danh duy nhất" được liên kết ở trên), việc kéo dữ liệu này ra khỏi SQL Server vào mã ứng dụng có thể không được xử lý đúng trong mã ứng dụng nếu cần mô phỏng hành vi so sánh SQL Server. Hành vi đó có thể được mô phỏng bằng cách chuyển đổi thành a SqlGuid
, nhưng liệu nhà phát triển có biết làm điều đó không?
Thứ hai: dựa trên tuyên bố sau
Nó làm điều này cho tất cả mọi thứ bao gồm các khóa chính.
Nói chung, tôi sẽ quan tâm đến hiệu năng hệ thống bằng cách sử dụng GUID làm PK thay vì làm Khóa thay thế cùng với sử dụng INT
hoặc thậm chí BIGINT
là PK. Và thậm chí quan tâm hơn nếu các PK GUID này là các Chỉ mục được nhóm.
Nhận xét sau đây, được OP đưa ra về câu trả lời của @ Rob, đưa ra một mối quan tâm bổ sung:
nó đã được di chuyển từ tôi nghĩ rằng MySQL
GUID có thể được lưu trữ ở 2 định dạng nhị phân khác nhau . Vì vậy, có thể có lý do cho mối quan tâm tùy thuộc vào:
Vấn đề về nơi biểu diễn nhị phân được tạo ra phải liên quan đến thứ tự byte của 3 trong số 4 "trường" đầu tiên. Nếu bạn theo liên kết ở trên với bài viết Wikipedia, bạn sẽ thấy RFC 4122 chỉ định sử dụng mã hóa "Big Endian" cho tất cả 4 trường, nhưng Microsoft GUID chỉ định sử dụng Endianness "Bản địa". Chà, kiến trúc Intel là Little Endian, do đó thứ tự byte cho 3 trường đầu tiên được đảo ngược từ các hệ thống theo RFC (cũng như GUID kiểu Microsoft được tạo trên các hệ thống Big Endian). Trường đầu tiên, "Dữ liệu 1", là 4 byte. Trong một Endianness, nó sẽ được biểu diễn dưới dạng (giả thuyết) 0x01020304
. Nhưng trong Endianness khác nó sẽ được 0x04030201
. Vì vậy, nếu cơ sở dữ liệu hiện tại 'BINARY(16)
biểu diễn nhị phân được tạo trên một hệ thống theo RFC, sau đó chuyển đổi dữ liệu hiện tại trong BINARY(16)
trường thành một UNIQUEIDENTIFIER
kết quả sẽ dẫn đến một GUID khác với những gì ban đầu được tạo. Điều này không thực sự gây ra vấn đề NẾU các giá trị không bao giờ rời khỏi cơ sở dữ liệu và các giá trị chỉ được so sánh với sự bình đẳng và không theo thứ tự.
Mối quan tâm với việc đặt hàng chỉ đơn giản là họ sẽ không theo cùng một trật tự sau khi chuyển đổi sang UNIQUEIDENTIFIER
. May mắn thay, nếu hệ thống ban đầu thực sự là MySQL thì việc đặt hàng không bao giờ được thực hiện trên biểu diễn nhị phân ở vị trí đầu tiên vì MySQL chỉ có một đại diện chuỗi của UUID .
Mối quan tâm với các giá trị chuỗi được sử dụng bên ngoài cơ sở dữ liệu lại nghiêm trọng hơn, nếu biểu diễn nhị phân được tạo bên ngoài Windows / SQL Server. Vì thứ tự byte có khả năng khác nhau, nên cùng một GUID ở dạng chuỗi sẽ dẫn đến 2 biểu diễn nhị phân khác nhau, tùy thuộc vào nơi chuyển đổi đó diễn ra. Nếu mã ứng dụng hoặc khách hàng được cung cấp GUID ở dạng chuỗi ABC
xuất phát từ dạng nhị phân 123
và biểu diễn nhị phân được tạo trên hệ thống theo RFC, thì biểu diễn nhị phân đó (nghĩa là 123
) sẽ chuyển thành dạng chuỗi DEF
khi được chuyển đổi thành a UNIQUEIDENTIFIER
. Tương tự, dạng chuỗi ban đầu của ABC
sẽ chuyển đổi thành dạng nhị phân 456
khi được chuyển đổi thành a UNIQUEIDENTIFIER
.
Vì vậy, nếu GUID không bao giờ rời khỏi cơ sở dữ liệu thì sẽ không có gì phải lo lắng về việc đặt hàng. Hoặc, nếu việc nhập từ MySQL được thực hiện bằng cách chuyển đổi dạng chuỗi (nghĩa là FCCEC3D8-22A0-4C8A-BF35-EC18227C9F40
) thì có thể ổn. Khác, nếu những GUID đó được trao cho khách hàng hoặc trong mã ứng dụng, bạn có thể kiểm tra xem họ chuyển đổi bằng cách lấy một cái và chuyển đổi qua SELECT CONVERT(UNIQUEIDENTIFIER, 'value found outside of the database');
và xem bạn có tìm thấy bản ghi dự kiến không. Nếu bạn không thể khớp các bản ghi thì bạn có thể phải giữ các trường như BINARY(16)
.
Trong tất cả khả năng sẽ không có vấn đề gì, nhưng tôi đang đề cập đến vấn đề này bởi vì trong những điều kiện phù hợp có thể có một vấn đề.
Và làm thế nào để GUID mới được chèn? Tạo trong mã ứng dụng?
Nếu lời giải thích trước đây về vấn đề tiềm ẩn liên quan đến việc nhập các biểu diễn nhị phân của GUID được tạo trên một hệ thống khác là một chút (hoặc rất nhiều) khó hiểu, hy vọng những điều sau đây sẽ rõ ràng hơn một chút:
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
Trong đầu ra được hiển thị ở trên, các giá trị "Chuỗi" và "Nhị phân" là từ cùng một GUID. Giá trị bên dưới dòng "Nhị phân" có cùng giá trị với dòng "Nhị phân", nhưng được định dạng theo cùng kiểu với dòng "Chuỗi" (nghĩa là đã xóa "0x" và thêm bốn dấu gạch ngang). So sánh các giá trị thứ nhất và thứ ba, chúng không hoàn toàn giống nhau, nhưng chúng rất gần nhau: hai phần bên phải giống hệt nhau, nhưng ba phần bên trái nhất thì không. Nhưng nếu bạn nhìn kỹ, bạn có thể thấy rằng đó là cùng một byte trong mỗi ba phần, chỉ theo một thứ tự khác nhau. Có thể dễ dàng hơn để xem nếu tôi chỉ hiển thị ba phần đầu tiên đó và đánh số byte để dễ dàng thấy thứ tự của chúng khác nhau như thế nào giữa hai biểu diễn:
Chuỗi = 1 5F 2 ED 3 23 4 BE - 5 E5 6 2C - 7 40 8 EE
Nhị phân = 4 BE 3 23 2 ED 1 5F - 6 2C 5 E5 - 8 EE 7 40 (trong Windows / SQL Server)
Vì vậy, trong mỗi nhóm, thứ tự của các byte được đảo ngược, nhưng chỉ trong Windows và cả SQL Server. Tuy nhiên, trên một hệ thống tuân thủ RFC, biểu diễn nhị phân sẽ phản ánh biểu diễn sting vì sẽ không có bất kỳ sự đảo ngược nào của thứ tự byte.
Dữ liệu được đưa vào SQL Server từ MySQL như thế nào? Dưới đây là một vài lựa chọn:
SELECT CONVERT(BINARY(16), '5FED23BE-E52C-40EE-8F45-49664C9472FD'),
CONVERT(BINARY(16), 0x5FED23BEE52C40EE8F4549664C9472FD),
CONVERT(BINARY(16), CONVERT(UNIQUEIDENTIFIER, '5FED23BE-E52C-40EE-8F45-49664C9472FD'));
Trả về:
0x35464544323342452D453532432D3430
0x5FED23BEE52C40EE8F4549664C9472FD
0xBE23ED5F2CE5EE408F4549664C9472FD
Giả sử nó là nhị phân thẳng thành nhị phân (tức là Chuyển đổi số 2 ở trên), thì GUID kết quả, nếu được chuyển đổi thành thực tế UNIQUEIDENTIFIER
, sẽ là:
SELECT CONVERT(UNIQUEIDENTIFIER, 0x5FED23BEE52C40EE8F4549664C9472FD);
Trả về:
BE23ED5F-2CE5-EE40-8F45-49664C9472FD
Sai chỗ nào. Và điều đó khiến chúng ta có ba câu hỏi:
Bạn luôn có thể quan tâm. ;)
Hệ thống có thể đã được di chuyển từ một số hệ thống khác không hỗ trợ định danh duy nhất. Có những thỏa hiệp nào khác mà bạn không biết?
Nhà thiết kế có thể không biết về loại định danh duy nhất. Những điều khác họ không biết về?
Về mặt kỹ thuật - nó không phải là một mối quan tâm chính.