如何仅针对datetime的日期部分创建唯一约束?

12

我正在为自己编写一个非常简单的博客引擎(因为我遇到的每个博客引擎都太复杂了)。 我想要能够通过其URL唯一地标识每篇文章,URL的格式类似于/2009/03/05/my-blog-post-slug。为了在数据层面实现这一点,我想创建一个由(Date, Slug)组成的复合唯一约束条件,其中Date仅是组合日期的日期部分(忽略时间)。 我有一些自己的想法(例如另一个列,可能是计算得出的,仅包含日期部分),但我来到SO想知道解决这个问题的最佳实践。

我怀疑SQL Server的版本在这里并不重要,但出于记录,我使用的是2008 Express(我希望有一个更具可移植性的解决方案)。

表结构:

create table Entries (
    Identifier int not null identity,
    CompositionDate datetime not null default getdate(),
    Slug varchar(128) not null default '',
    Title nvarchar(max) not null default '',
    ShortBody nvarchar(max) not null default '',
    Body nvarchar(max) not null default '',
    FeedbackState tinyint not null default 0,
    constraint pk_Entries primary key(Identifier),

    constraint uk_Entries unique (Date, Slug) -- the subject of the question
)

选定的解决方案:

我认为marc的解决方案更适合,考虑到这个问题是关于2008年的。但是,我会采用整数方法(但不是使用INSERT,因为它不能确保数据完整性;我将使用预计算的整数列),因为我认为从客户端(在查询中)使用整数更容易处理。

谢谢大家。

create table Entries (
    Identifier int not null identity,
    CompositionDate smalldatetime not null default getdate(),
    CompositionDateStamp as cast(year(CompositionDate) * 10000 + month(CompositionDate) * 100 + day(CompositionDate) as int) persisted,
    Slug varchar(128) not null default '',
    Title nvarchar(max) not null default '',
    ShortBody nvarchar(max) not null default '',
    Body nvarchar(max) not null default '',
    FeedbackState tinyint not null default 0,
    constraint pk_Entries primary key(Identifier),
    constraint uk_Entries unique (CompositionDateStamp, Slug)
)
go
4个回答

14

在SQL Server 2008中,有一种名为“DATE”的新数据类型 - 您可以使用该列并创建索引。

当然,您也可以向表中添加一个计算列(类型为“DATE”),并将DATETIME列的日期部分填充到该计算列中,使其持久化,并对其进行索引。应该可以正常工作!

类似于这样:

ALTER TABLE dbo.Entries
   ADD DateOnly as CAST(CompositionDate AS DATE) PERSISTED

CREATE UNIQUE INDEX UX_Entries ON Entries(DateOnly, Slug)

Marc

:马克

有没有更便携的解决方案?可能还能在2005年使用。 - Mehrdad Afshari
据我所知,没有 :-) 除非你想开始编写一些代码来剥离DATETIME列的时间部分... - marc_s
或者,您可以创建三个字段用于年、月、日,并将这些字段填充为对“DATEPART(...., CompositionDate)”的调用结果,然后在这三列加上“Slug”创建您的唯一索引。 - marc_s
我喜欢这个解决方案,但插入记录时会付出什么样的性能惩罚?那一列不是必须即时计算吗?虽然可能是一个无关紧要的问题,但仍然需要考虑... - cbmeeks
@cbmeeks:该列将在插入和/或更新时计算 - 然后,仅在这时 - 一次。计算出的值像表中的任何其他值一样被持久化(例如存储)。 - marc_s

2

如果您正在使用2008版本,请像Marc建议的那样使用日期数据类型。否则,一种更简单的解决方案是拥有一个非计算列(这意味着您必须在插入时填充它),该列使用YYYYMMDD格式的日期。这是一个整数数据类型,易于使用且占用空间较小。


2
这是一个不错的解决方案,不过我会让它自动计算 = DATEPART(year, date) * 1000000 + DATEPART(month, date) * 100 + DATEPART(day, date)。 - Mehrdad Afshari

1

对于 SQL 2005,您可以执行与 marc_s 建议的基本相同的操作,只需使用标准 DateTime 即可。它看起来应该像这样(未经测试的代码):

ALTER TABLE Entries ADD
    JustTheDate AS DATEADD(day, DATEDIFF(day, 0, CompositionDate), 0) NOT NULL PERSISTED

在 (JustTheDate, Slug) 上创建你的索引。
注意:那里的 DATEADD/DATEDIFF 语句仅计算 CompositionDate 的日期。

0
多年来,我们在SQL Server中使用计算列时遇到过各种问题,因此我们已经停止使用它们。
相反,您可以使用带有仅日期的列的视图,并在该视图的列上放置唯一索引。
(一个可能有用的副作用是您可以让视图排除某些行,因此您可以实现诸如“DateColumn必须唯一,但排除WHERE DateColumn IS NULL”的内容)
现有的CompositionDate列可以拆分为两个字段-CompositionDate和CompositionTime,并且如果需要,检索视图将它们合并在一起,这将允许Date-only列上的本机索引。
(这可以在SQL 2005及更早版本中使用DateTime实现-尽管仅为日期或时间稍微浪费,而不是两者)
最后,您可以使用INSERT / UPDATE触发器,强制执行不存在具有重复CompositionDate(仅日期部分)的其他记录。

虽然你的回答对于大型项目来说完全有效,但我不想过度设计和不必要地增加这个小项目的复杂性。在计算列上创建唯一索引完美地满足了这个需求。 - Mehrdad Afshari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接