【问题标题】:SQL Constraint : only one record per day for a particular keySQL 约束:特定键每天只有一条记录
【发布时间】:2012-02-23 21:37:58
【问题描述】:

有一种方法可以创建一个约束,或者我可以在 FK 列的一天中只有一条记录吗?即。

PK(IDENTITY(1,1))  FK_FIELD DATETIME
------------------------------
1                  1        2012-02-22 4:50 p.m.
2                  2        2012-02-23 12:00 p.m.
3                  2        2012-02-23 9:00 p.m.  -- This one should not be allowed because the FK_Field 2 would have 2 record in one day.
4                  1        2012-02-23 08:00 a.m 

希望有人了解我想要归档的内容。

提前致谢。

【问题讨论】:

  • 添加一个没有时间的日期字段并将其添加到键中。

标签: sql sql-server sql-server-2005


【解决方案1】:

考虑使用:

  • 1 个或多个持久计算列
  • 对持久计算列的唯一约束

各种持久列的一些示例(非常感谢 Aaron Bertrand):

  • 一年(某日)持续
  • M 月(某日)持续
  • 持续一个月(某天)
  • YMD AS YEAR(SOMEDATE) * 10000 + MONTH(SOMEDATE) * 100 + DAY(SOMEDATE) 持续
  • SOMEDATEDIFF 作为 DATEDIFF(DAY, 0, SOMEDATE) 持续存在
  • SOMEDATESMALL AS DATEADD(DAY, 0, DATEDIFF(DAY, 0, SOMEDATE)) 持久化
  • SOMEDATESMALL AS CONVERT(DATETIME, FLOOR(CONVERT(FLOAT, SOMEDATE, 105)), 105) 持续
  • SOMEDATESMALL AS CONVERT(DATE, SOMEDATE) PERSISTED -- 需要 SQL Server 2008

注意这些示例经过精心挑选,以确保功能确定性。

这是一个示例 SQL:

CREATE TABLE DATA
(
     ID INT NOT NULL IDENTITY,
     FK_FIELD INT NOT NULL,
     SOMEDATE DATETIME NOT NULL,
     SOMEDATESMALL AS DATEADD(DAY, 0, DATEDIFF(DAY, 0, SOMEDATE)) PERSISTED,
     CONSTRAINT PK_DATA PRIMARY KEY (ID),
     CONSTRAINT UQ_DATA_SOMEDATESMALL UNIQUE (FK_FIELD, SOMEDATESMALL)
);

如果您尝试了 INSERT,这就是您得到的结果:

INSERT INTO DATA (FK_FIELD, SOMEDATE) VALUES (1, '2012-02-22 4:50PM'); -- SUCCESS
INSERT INTO DATA (FK_FIELD, SOMEDATE) VALUES (2, '2012-02-23 12:00PM'); -- SUCCESS
INSERT INTO DATA (FK_FIELD, SOMEDATE) VALUES (2, '2012-02-23 9:00PM'); -- CAUGHT
INSERT INTO DATA (FK_FIELD, SOMEDATE) VALUES (1, '2012-02-23 8:00AM'); -- SUCCESS

SQL Server Management Studio 中显示的消息是:

消息 2627,第 14 层,状态 1,第 3 行
违反 UNIQUE KEY 约束“UQ_DATA_SOMEDATESMALL”。
无法在对象“dbo.DATA”中插入重复键。
声明已终止。

【讨论】:

  • 好主意,但是三个 int 列比 smalldatetime 列要贵得多。 YMD as CONVERT(SMALLDATETIME, DATEDIFF(DAY, '19000101', SOMEDATE))我会将此作为答案发布,但我认为您的答案太相似了。
  • +1 Aaron 折叠到单个字段是个好建议。因为我想使用唯一约束(不是唯一索引),所以列必须是确定性的,所以我不得不避免使用 CONVERT 函数。
  • 是的,好点,但是如果您将19000101 更改为0,您仍然可以使用SMALLDATETIME - 不确定性不是由CONVERT 本身引起的,而是由对字符串文字(在 YYYYMMDD 的情况下不能被误解,但我想 SQL Server 并没有真正查看字符串)。
  • 顺便说一句,我推动SMALLDATETIME 的原因是该索引可以用于其他事情,例如日期范围查询 - 如果您要为索引支付 4 个字节,它也可以用于其他目的。 :-)
  • 还有一件事,如果 OP 在 SQL Server 2008 上(哦,我多么希望每个人都指定 - 通用的 sql-server 标记应该被拒绝)你可以只说 AS CONVERT(DATE, SOMEDATE) 这消除了需要修剪时间。
【解决方案2】:

您可以创建一个检查约束,该约束使用一个用户定义的函数来检查整个表是否存在今天的条目。

CREATE TABLE #demo (
    some_date_field DATETIME NULL 
        CHECK (dbo.check_only_one(GETDATE())
)

【讨论】:

  • 听起来是个好主意,我喜欢@Bycycle 和“Spark 答案,但我不喜欢的是表会很大,计算列会使用很多将用作约束的空间,这就是为什么我根本不喜欢它们。用户定义函数的返回类型应该是什么?
  • 返回可以像一个布尔值一样简单,指示插入它是否有效
【解决方案3】:

这是一种方法

create table t1 (pk int identity(1,1),fk_field int,theDate DateTime,
                 theDay as convert(varchar(12),theDate,102))
create unique index noDupes on t1 (fk_field,theDay)

您也可以按照 Aaron 的建议使用约束

create table t1 (pk int identity(1,1),fk_field int,theDate DateTime,
                     theDay as convert(varchar(12),theDate,102)
    CONSTRAINT [noDupes] UNIQUE  ( [fk_field], [theDay])        )               
  • 添加一个仅包含日期的日期部分的计算列。
  • 然后在键和计算域上创建一个唯一索引

    insert into  t1 values (1,getDate())        
    insert into  t1 values (2,getDate())          
    insert into  t1 values (2,getDate())    -- ERROR    
    insert into  t1 values (3,getDate())
    

【讨论】:

  • 如果它是一个约束(确实如此),我强烈建议将其编写为一个约束,而不是一个索引。虽然在幕后实现是相同的,但逻辑意图却大不相同。
  • 谢谢亚伦,好点,我已经更新了答案以显示约束
【解决方案4】:

我会尝试在数据库的 2 个不同字段中或按组合将日期与时间分开。然后我会在日期上添加一个唯一约束。只要日期在您添加约束的列中,以下内容将阻止对同一日期进行任何输入。

ALTER TABLE XYZ
    ADD CONSTRAINT UNIQUE( the date field name)

还有一个非常有趣的线程,你应该检查一个类似的问题,在这里解决了How to create a unique constraint just on the date part of a datetime?

希望对你有帮助

【讨论】:

  • 我认为这可能是一个好方法,但是要将时间存储在不同的列中,我应该有一个时间列,并且仅从 sql server 2008 开始支持,抱歉我没有指定,如果我愿意,我可以将它们存储为数字,我会考虑它,但我认为我需要更改很多东西才能实现这一点。不管怎样,感谢您的时间!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-02
  • 1970-01-01
相关资源
最近更新 更多