Indexing 确保MS SQL中多个大URL字段的唯一性

Indexing 确保MS SQL中多个大URL字段的唯一性,indexing,unique-constraint,sql-server,Indexing,Unique Constraint,Sql Server,我有一个具有以下定义的表: CREATE TABLE url_tracker ( id int not null identity(1, 1), active bit not null, install_date int not null, partner_url nvarchar(512) not null, local_url nvarchar(512) not null, public_url nvarchar(512) not null,

我有一个具有以下定义的表:

CREATE TABLE url_tracker (
    id int not null identity(1, 1),
    active bit not null,
    install_date int not null,
    partner_url nvarchar(512) not null,
    local_url nvarchar(512) not null,
    public_url nvarchar(512) not null,
    primary key(id)
);
我有一个要求,这三个URL总是唯一的-任何单独的URL可以出现很多次,但三者的组合必须是唯一的(对于给定的一天)

起初我认为我可以做到这一点:

CREATE UNIQUE INDEX uniques ON url_tracker 
(install_date, partner_url, local_url, public_url);
然而,这给了我一个警告:

Warning! The maximum key length is 900 bytes. The index 'uniques' has maximum
length of 3076 bytes. For some combination of large values, the insert/update
operation will fail.
我了解了
INCLUDE
参数到
CREATE INDEX
,但是根据将命令转换为使用
INCLUDE
不会强制URL的唯一性

CREATE UNIQUE INDEX uniques ON url_tracker (install_date)
INCLUDE (partner_url, local_url, public_url);
如何在几个相对较大的nvarchar字段上强制唯一性


决议 因此,从评论、回答和更多研究中,我得出结论,我可以做到:

CREATE TABLE url_tracker (
    id int not null identity(1, 1),
    active bit not null,
    install_date int not null,
    partner_url nvarchar(512) not null,
    local_url nvarchar(512) not null,
    public_url nvarchar(512) not null,
    uniquehash AS HashBytes('SHA1',partner_url+local_url+public_url) PERSISTED,
    primary key(id)
);
CREATE UNIQUE INDEX uniques ON url_tracker (install_date,uniquehash);

想法?

我会用URL的名称创建一个计算列,然后在此列上创建一个唯一的索引/约束。考虑将哈希变成一个持久化的计算列。插入后不必重新计算。

遵循评论中对话的想法。假设您可以将URL的数据类型更改为
VARCHAR(900)
(或者
NVARCHAR(450)
,如果您真的认为您需要Unicode URL),并且对URL的长度限制感到满意,那么此解决方案可以工作。这也假定SQL Server 2008或更高版本。请始终指定您使用的版本;还不够具体,因为解决方案可能因版本而异

设置:

USE tempdb;
GO

CREATE TABLE dbo.urls
(
    id INT IDENTITY(1,1) PRIMARY KEY,
    url VARCHAR(900) NOT NULL UNIQUE
);

CREATE TABLE dbo.url_tracker 
(
    id INT IDENTITY(1,1) PRIMARY KEY,
    active BIT NOT NULL DEFAULT 1,
    install_date DATE NOT NULL DEFAULT CURRENT_TIMESTAMP,
    partner_url_id INT NOT NULL REFERENCES dbo.urls(id),
    local_url_id   INT NOT NULL REFERENCES dbo.urls(id),
    public_url_id  INT NOT NULL REFERENCES dbo.urls(id),
    CONSTRAINT unique_urls UNIQUE
    (
        install_date,partner_url_id, local_url_id, public_url_id
    )
);
插入一些URL:

INSERT dbo.urls(url) VALUES
    ('http://msn.com/'),
    ('http://aol.com/'),
    ('http://yahoo.com/'),
    ('http://google.com/'),
    ('http://gmail.com/'),
    ('http://stackoverflow.com/');
现在让我们插入一些数据:

-- succeeds:
INSERT dbo.url_tracker(partner_url_id, local_url_id, public_url_id)
VALUES (1,2,3), (2,3,4), (3,4,5), (4,5,6);

-- fails:
INSERT dbo.url_tracker(partner_url_id, local_url_id, public_url_id)
VALUES(1,2,3);
GO

/*
    Msg 2627, Level 14, State 1, Line 3
    Violation of UNIQUE KEY constraint 'unique_urls'. Cannot insert duplicate key 
    in object 'dbo.url_tracker'. The duplicate key value is (2011-09-15, 1, 2, 3).
    The statement has been terminated.
*/

-- succeeds, since it's for a different day:
INSERT dbo.url_tracker(install_date, partner_url_id, local_url_id, public_url_id)
VALUES('2011-09-01',1,2,3);
清理:

DROP TABLE dbo.url_tracker, dbo.urls;
现在,如果900字节不够,您可以稍微更改URL表:

CREATE TABLE dbo.urls
(
    id INT IDENTITY(1,1) PRIMARY KEY,
    url VARCHAR(2048) NOT NULL,
    url_hash AS CONVERT(VARBINARY(32), HASHBYTES('SHA1', url)) PERSISTED,
    CONSTRAINT unique_url UNIQUE(url_hash)
);
其余的不必改变。如果你尝试插入相同的URL两次,你会得到类似的违规行为,例如

INSERT dbo.urls(url) SELECT 'http://www.google.com/';
GO
INSERT dbo.urls(url) SELECT 'http://www.google.com/';
GO

/*
    Msg 2627, Level 14, State 1, Line 1
    Violation of UNIQUE KEY constraint 'unique_url'. Cannot insert duplicate key 
    in object 'dbo.urls'. The duplicate key value is
    (0xd111175e022c19f447895ad6b72ff259552d1b38).
    The statement has been terminated.
*/

您可能需要为每一个获取一个哈希值并比较哈希值。但这会对性能产生负面影响。@JNK我喜欢你的建议,你应该将其作为answer@JNK我也喜欢。不过,您可以将散列存储为持久化的计算列,以减少影响。可能的解决办法是降低租金。这是什么版本的SQL Server?您也可以将URL存储在单独的表中。给他们一个代理
id
,只需为idsp的组合编制索引,一个
INCLUDE
列只需“随便便”就可以帮助防止不必要的查找-这就是为什么不能使用它来强制唯一性;它不是键的一部分。@dimo414我认为您可能需要将HASHBYTES强制转换为VARBINARY(20)-此外,如果您认为有任何方法可以使用三个不同的URL生成相同的长字符串,您可能需要在连接过程中以某种方式对其进行唯一分隔-即ab123不明确,可能来自a、b、123或ab、1、23或a、b1,23等等。考虑到每一个都以http://开头,我不认为这是可能的,但如果你认为这很关键,我可以用|来界定它们。