在 Sql 服务器中查找重复的唯一标识符的最快方法是什么?

What is the fastest way to look for duplicate uniqueidentifier in Sql Server?

我们对一个非常大的数据库中的每条记录使用 uniqueidentifier。出于业务原因,我们需要确保 uniqueidentifier 永远不会被使用超过一次,但出于性能原因,我们将 bigint 作为 primary key.

在 Sql 服务器 table 中测试 uniqueidentifer 是否存在的最快方法是什么?

**

< 0.05ms 在单个标准 S0 Sql Azure 实例上验证来自 100,000,000 行 的唯一标识符。

**

免责声明: 以下方法可能需要根据您的业务需求进行调整,目前仅 运行 在 Sql S0 实例(10 个 DTU)上的 Azure 暂存环境。目标是概念验证。

(请参阅下面的样板 CREATE To 脚本)

方法: 创建一个 table 专门用于存储数据库中存在的任何 uniqueidentifiers。这个 table 将进行大量优化,以增强我们的 uniqueidentifer 验证器。

table 将有四列。一个用于 uniqueidentifier,另外三个作为单个数字 binary 计算列,用于存储 uniqueidentifier.

的三个数字中的每一个

然后我们将在前三位数字列上创建聚集索引,uniqueidentifer 作为聚集索引的最后一列。

我们最后将创建一个存储过程,它将采用 uniqueidentifer 并将前三位数字分解为 binary 数据类型并执行查找,利用 B 树结构根据我们的聚簇索引磁盘上的数据。

Table 创建到(带聚簇索引):

USE [MyDatabase]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ARITHABORT ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[UniqueIds](
    [Guid] [uniqueidentifier] NOT NULL,
    [Char01]  AS (CONVERT([char](1),substring(CONVERT([char](36),[Guid]),(1),(1)))) PERSISTED NOT NULL,
    [Char02]  AS (CONVERT([char](1),substring(CONVERT([char](36),[Guid]),(2),(1)))) PERSISTED NOT NULL,
    [Char03]  AS (CONVERT([char](1),substring(CONVERT([char](36),[Guid]),(3),(1)))) PERSISTED NOT NULL,
 CONSTRAINT [PK_UniqueIds] PRIMARY KEY CLUSTERED 
(
    [Char01] ASC,
    [Char02] ASC,
    [Char03] ASC,
    [Guid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)

GO

SET ANSI_PADDING OFF
GO

验证存储过程:

CREATE PROCEDURE [dbo].[UniqueIds.CountIds]
    @Guid uniqueidentifier,
    @IdCount bigint OUTPUT
AS
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Break out uniqueidentifier down into the first three characters for indexed lookup.
    DECLARE @SubChar char(3) = CONVERT([char](36),@Guid);
    DECLARE @Char01 char(1) = @SubChar;
    DECLARE @Char02 char(1) = SUBSTRING(@SubChar,2,1);
    DECLARE @Char03 char(1) = RIGHT(@SubChar,1);

    -- Check if GUID already exists
    (SELECT TOP 1 @IdCount=1 FROM UniqueIds WHERE Char01=@Char01 AND Char02=@Char02 AND Char03=@Char03 AND [Guid]=@Guid);

INSERTS 的存储过程:

CREATE PROCEDURE [dbo].[UniqueIds.Insert]
    @Guid uniqueidentifier
AS
BEGIN TRY
    BEGIN TRAN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Check if GUID already exists
    DECLARE @IdCount bigint;
    EXEC    [UniqueIds.CountIds]
            @Guid=@Guid,
            @IdCount = @IdCount OUTPUT

    IF @IdCount IS NULL
        INSERT INTO UniqueIds
        (
            [Guid]
        )
        VALUES
        (
            @Guid
        )
    ELSE
        THROW 60000, '[Guid] must be unique. Another unique identifier with the same signature exists.', 1;

    COMMIT

END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK;
    THROW;
END CATCH

插入新的 uniqueidentifier(示例):

DECLARE @id uniqueidentifier
SET @id=NEWID()
EXEC    [UniqueIds.Insert]
        @Guid=@id

关于实施的最终决定:

每次在数据库中的任何位置插入新的 uniqueidentifier 时,只需将其包装在回滚事务中并调用用于插入 uniqueidentifer 的存储过程。这将调用我们的验证存储过程,如果它失败,它会抛出一个错误。您的回滚将确保不会保留任何其他内容。

示例:

CREATE PROCEDURE [dbo].[Bases.Insert]
    @Guid uniqueidentifier,
    @AccountId bigint=0,
    @ModifierId bigint=0,
    @ScopeIdentity bigint OUTPUT
AS
**BEGIN TRY
    BEGIN TRAN**
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON

        **EXEC  [dbo].[UniqueIds.Insert]
        @Guid = @Guid;**

        -- Insert the base row
        INSERT INTO Bases
        (
            [Guid],
            [State],
            Utc,
            AccountId,
            ModifierId
        )
        VALUES
        (
            @Guid,
            0,
            GETUTCDATE(),
            @AccountId,
            @ModifierId
        );

    **COMMIT;**

    SELECT @ScopeIdentity = CAST(SCOPE_IDENTITY() As bigint);

**END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK;
    THROW;
END CATCH**

**

Result: validating the uniqueness of a uniqueidentifier from 100 MILLION rows consistently takes < 0.05ms

**

对我来说,我将模型 属性 从我的核心项目更改为 ICollection<>,然后我添加了迁移和更新数据库,这解决了我的问题