【问题标题】:How to create a deterministic uniqueidentifier (GUID) from an integer value如何从整数值创建确定性唯一标识符 (GUID)
【发布时间】:2018-08-28 15:07:07
【问题描述】:

注意:这与数据库设计无关,也与 GUID 的一般使用无关。这是关于在 Microsoft SQL 服务器上为测试数据确定性地创建此类 GUID。

我们正在将我们的数据库从整数标识符迁移到 uniqueidentifier 数据类型。

出于测试目的,我们希望将我们的测试数据集迁移到已知的 GUID 值,确定性地基于我们以前的整数值

UPDATE Unit
SET UnitGuid = NEWID(UnitId)

显然这不会立即生效。 如何使用 UnitId 创建确定性 GUID?

【问题讨论】:

  • GUID 不应“基于某些值”创建。如果需要,您可以根据该值制作 HASH,但不要将其称为 guid。 @Lucasz 建议的映射表是在测试环境和产品环境上执行此操作的方法。
  • 这样做要非常小心。如果这成为您的聚集索引,则会出现一些非常严重的性能问题。 sqlskills.com/blogs/kimberly/…

标签: sql sql-server guid


【解决方案1】:

您可以创建键盘映射表:

CREATE TABLE tab_map(id_old INT PRIMARY KEY, guid UNIQUEIDENTIFIER);

INSERT INTO tab_map(id_old, guid)
SELECT id, NEWID()
FROM src_table;

DBFiddle Demo

之后,您可以使用简单查询或使用函数包装:

SELECT guid
FROM tab_map
WHERE id_old = ?

【讨论】:

  • 虽然这有点帮助,但它错过了我的主要观点 - 具有确定性的 GUID。请记住,这仅适用于测试数据,我想证明我的查询仍然按预期工作。对于生产,我们将使用全新的数据重新开始。
【解决方案2】:

停止从"string" perspective 考虑问题。 int 由 4 个字节组成。 uniqueidentifier 由 16 个字节组成。您可以轻松获取 12 个固定字节并将 int 中的四个字节附加到这些字节的末尾,并获得适用于所有 int 值的解决方案:

declare @Unit table
(
UniqueColumn UNIQUEIDENTIFIER DEFAULT NEWID(),
Characters VARCHAR(10),
IntegerId int
)

-- Add *3* data rows
INSERT INTO @Unit(Characters, IntegerId) VALUES ('abc', 1111),('def', 2222),('ghi',-17)

-- Deterministically creates a uniqueidentifier value out of an integer value. 
DECLARE @GuidPrefix binary(12) = 0xefbeadde0000000000000000
UPDATE @Unit 
    SET UniqueColumn = CONVERT(uniqueidentifier,@GuidPrefix + CONVERT(binary(4),IntegerId))

-- Check the result
SELECT * FROM @Unit

结果:

UniqueColumn                         Characters IntegerId
------------------------------------ ---------- -----------
DEADBEEF-0000-0000-0000-000000000457 abc        1111
DEADBEEF-0000-0000-0000-0000000008AE def        2222
DEADBEEF-0000-0000-0000-0000FFFFFFEF ghi        -17

(由于各种原因,我们必须以与默认使用的顺序不同的顺序提供前四个字节,将uniqueidentifier显示为字符串,这就是为什么如果我们想显示DEADBEEF,我们必须以efbeadde) 开始我们的二进制文件

当然,还要插入通常的警告,如果您正在创建 guid/唯一标识符,但没有使用规定的方法之一来生成它们,那么您不能假设任何关于唯一性的通常保证。

【讨论】:

  • 这是一个很好的答案,我接受它。对我来说,在我的应用程序中,在 GUID 中使用“逐字”id 值非常方便,我将实施我的解决方案。但同样,这仅用于测试数据!
【解决方案3】:

我最终自己解决了这个问题。这是我的解决方案以供将来参考:

我以deadbeef-0000-0000-0000- 的形式创建 GUID 的前缀部分,然后将 Id 列的整数值的“字符串化”、零填充版本附加到它,例如 000000000001,结果是

DEADBEEF-0000-0000-0000-000000000001

在这个例子中。

这是对整个表执行此操作的 SQL 命令:

-- Deterministically creates a uniqueidentifier value out of an integer value. 
DECLARE @GuidPrefix nvarchar(max) = N'deadbeef-0000-0000-0000-';  -- without the last 12 digits
UPDATE Unit 
    SET UniqueColumn = 
    (SELECT @GuidPrefix + RIGHT('000000000000' + CAST(IntegerId AS NVARCHAR (12)), 12 ) AS NUMBER_CONVERTED)

警告:

  • 此实现仅适用于正 int 值(向上 最大为 2147483647)
  • 这仅用于测试数据!用途是 强烈反对生产数据!

这是一个完整的工作示例:

-- Create an example table with random GUID's
CREATE TABLE Unit
(
UniqueColumn UNIQUEIDENTIFIER DEFAULT NEWID(),
Characters VARCHAR(10),
IntegerId int
)

-- Add 2 data rows
INSERT INTO Unit(Characters, IntegerId) VALUES ('abc', 1111)
INSERT INTO Unit(Characters, IntegerId) VALUES ('def', 2222)

-- Deterministically creates a uniqueidentifier value out of an integer value. 
DECLARE @GuidPrefix nvarchar(max) = N'deadbeef-0000-0000-0000-';  -- without the last 12 digits
UPDATE Unit 
    SET UniqueColumn = 
    (SELECT @GuidPrefix + RIGHT('000000000000' + CAST(IntegerId AS NVARCHAR (12)), 12 ) AS NUMBER_CONVERTED)

-- Check the result
SELECT * FROM Unit

结果:

UniqueColumn                            Characters IntegerId
--------------------------------------- ---------- ---------
DEADBEEF-0000-0000-0000-000000001111    abc        1111
DEADBEEF-0000-0000-0000-000000002222    def        2222

【讨论】:

    猜你喜欢
    • 2016-10-18
    • 2010-11-28
    • 2011-08-29
    • 1970-01-01
    • 1970-01-01
    • 2018-10-14
    • 2014-02-28
    • 2023-02-25
    • 2013-09-09
    相关资源
    最近更新 更多