在现有表的 nvarchar(max) 字段上添加唯一索引的最佳方法是什么?

mar*_*are 4 sql-server unique-constraint

我发现自己所处的情况是由于某种原因我将错误的数据输入到数据库中。我想通过UNIQUE INDEXUFID字段上添加到用户表 a 来保持数据完整性。

自然地,我生成了一个脚本,以非破坏性的方式删除任何重复的记录:

update [user]
set UFID = UFID + '_dup_removal'
where ufid in (
  select ufid 
  from [user]
  group by ufid
  having count(ufid) > 1
)
Run Code Online (Sandbox Code Playgroud)

然后我发现该字段无法在UNIQUE INDEX.

那么现在获得结果的最佳方法是什么,我有用户表和数据并添加了UNIQUE INDEX.

注意:它应该以允许轻松逆转的方式完成。

spa*_*dba 7

正如您所发现的,varchar(max) 列不能被索引。您将需要索引其他内容。

它可以是数据的缩短版本(只有您可以判断这是否可以接受)或数据的散列版本。哈希冲突的几率非常低。

USE tempdb;

IF OBJECT_ID('user') IS NOT NULL
    DROP TABLE [user];

CREATE TABLE [user] (
    UFID varchar(max)
)

INSERT INTO [user] VALUES(1),(2),(3),(4),(4),(4),(3),(5);


WITH ranked_users AS (
    SELECT *, RN = ROW_NUMBER() OVER(PARTITION BY UFID ORDER BY (SELECT NULL))
    FROM [user]
)
update ranked_users
set UFID = UFID + '_dup_removal_' + CAST(RN AS varchar(10))
where RN > 1;


--CREATE UNIQUE INDEX IX_UFID ON [user](UFID) -- FAILS

-- Solution 1: add a computed column with the size trimmed down 
ALTER TABLE [user] ADD shortened_ufid AS CAST(UFID AS varchar(900))

CREATE UNIQUE INDEX IX_shortened_UFID ON [user](shortened_ufid) 

-- Solution 2: add a computed column with the hashed version of the data
ALTER TABLE [user] ADD hashed_ufid AS CAST(HASHBYTES('SHA1', UFID) AS bigint)

CREATE UNIQUE INDEX IX_hashed_UFID ON [user](hashed_UFID) 

SELECT *
FROM [user]
GO
Run Code Online (Sandbox Code Playgroud)

另一种选择是清理数据,然后确保不会使用触发器插入重复项。不过,这将是非常缓慢的。

-- solution 3: use a trigger
CREATE TRIGGER TR_no_dupes ON [user]
FOR INSERT, UPDATE
AS
BEGIN


    IF EXISTS (
        SELECT 1
        FROM [user] 
        WHERE UFID IN (
            SELECT UFID 
            FROM inserted
        )
        GROUP BY UFID
        HAVING COUNT(*) > 1
    )
    ROLLBACK;

END
Run Code Online (Sandbox Code Playgroud)