【问题标题】:How to create a unique constraint just on the date part of a datetime?如何仅在日期时间的日期部分创建唯一约束?
【发布时间】:2010-10-11 20:34:29
【问题描述】:

我正在编写一个非常简单的博客引擎供自己使用(因为我遇到的每个博客引擎都太复杂了)。我希望能够通过其 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
)

选定的解决方案:

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

谢谢你们。

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

【问题讨论】:

    标签: sql sql-server sql-server-2008 datetime unique-constraint


    【解决方案1】:

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

    当然,您也可以将“DATE”类型的计算列添加到表中,只需将 DATETIME 列的日期部分填充到该计算列中,使其成为 PERSISTED 并为其编制索引。应该可以正常工作!

    类似的东西:

    ALTER TABLE dbo.Entries
       ADD DateOnly as CAST(CompositionDate AS DATE) PERSISTED
    
    CREATE UNIQUE INDEX UX_Entries ON Entries(DateOnly, Slug)
    

    马克

    【讨论】:

    • 有更便携的解决方案吗?可能在 2005 年也可以使用。
    • 据我所知,没有 :-) 除非您想开始编写一些代码来删除 DATETIME 列的时间部分...
    • 或者:您可以为年、月、日创建三个字段,并用调用“DATEPART(...., CompositionDate) 的结果填充这些字段,然后在这三个字段上创建唯一索引列加上“Slug”。
    • 我喜欢这个解决方案,但是在插入记录时你会付出什么样的性能损失?该列是否必须即时计算?当然,可能是一个 m00t 点,但仍然......
    • @cbmeeks:该列将被计算 when 插入和/或 when 更新 - 然后,只有这样 - 一次。计算值与表中的任何其他值一样持久化(例如存储)
    【解决方案2】:

    对于 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 的日期。

    【讨论】:

      【解决方案3】:

      由于您是 2008 年,请按照 Marc 的建议使用 Date 数据类型。否则,更简单的解决方案是使用格式为 YYYYMMDD 的日期的非计算列(这意味着您必须在 INSERT 上填充它)。这是一种整数数据类型,体积小且易于使用。

      【讨论】:

      • 这是一个很好的解决方案,但是我会自动计算 = DATEPART(year, date) * 1000000 + DATEPART(month, date) * 100 + DATEPART(day, date)
      【解决方案4】:

      多年来,我们在 SQL Server 中遇到了各种计算列问题,因此我们已停止使用它们。

      您可以将 VIEW 与仅用于日期的列一起使用 - 并在该 VIEW 的列上放置唯一索引。

      (一个可能有用的副作用是您可以让 VIEW 排除某些行 - 因此您可以实现诸如“DateColumn 必须是唯一的,但排除 WHERE DateColumn IS NULL)之类的内容)

      现有的 CompositionDate 列可以拆分为两个字段 - CompositionDate 和 CompositionTime - 以及一个检索视图,如果您需要,可以将它们重新组合在一起 - 然后允许在仅日期列上使用本机索引

      (这可以在 SQL 2005 和更早的版本中使用 DateTime 来实现 - 虽然对于日期或时间来说有点奢侈,而不是两者兼而有之)

      最后,您可以使用 INSERT / UPDATE 触发器强制不存在具有重复 CompositionDate 的其他记录(仅限日期部分)

      【讨论】:

      • 虽然您的回答可能对大型项目完全有效,但我不想过度设计和不必要地增加这样一个小项目的复杂性。计算列上的唯一索引非常适合这种需求。
      猜你喜欢
      • 2021-03-27
      • 1970-01-01
      • 1970-01-01
      • 2014-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-05
      相关资源
      最近更新 更多