SQL Server中计算列的最佳实践

12
我正在使用一个用户表,并希望添加一个“试用期结束日期”。每个新用户加入后都有两个完整月的时间作为试用期。我看到可以在用户表的列中放置一个公式,但我想知道是否应该编写脚本来更新此日期,或者这是使用计算列的可接受时间。我会因为成就而偶尔更新用户行,但申请日期永远不会更改/更新。
我的问题是:在这种情况下使用计算列是否是一个好习惯,它是否会在每次我更新该行时重新计算(即使我不计划更新申请日期)?我不想在未来更新该行时创建更多开销。
我在Probation End Date列定义中使用的公式如下:
(dateadd(day,(-1),dateadd(month,(3),dateadd(day,(1)-datepart(day,[APP_DT]),[APP_DT]))))

“加入日期”和“申请日期”是同一件事吗?您是否希望稍后能够独立于其他日期列更新试用期结束日期? - Aaron Bertrand
2个月的政策会改变吗?将来可能会变成1个月、3个月或其他值吗? - Damien_The_Unbeliever
是的,JoinDate/AppDate是同一件事(抱歉,我会交替使用它们)。我永远不会覆盖日期,但是根据marc_s的观点,由于它不会被更新,我将在插入脚本的一部分中包含它。我需要对此进行索引,如果没有持久化,那么这将无法正常工作。非常感谢您的快速回复! - Brian
是的,政策可能会发生变化,但如果发生变化,它将适用于所有现有用户。非常好的观点。 - Brian
3个回答

15

考虑到一旦设定,这个日期很可能永远不会改变,因此它可能不适合作为一个计算列。

毕竟:一旦你向表中插入一行数据,你就可以轻松地计算出“试用期结束日期”(例如在触发器中),一旦设置,该日期将永远不会更改。

因此,虽然你肯定可以这样做,但我可能更喜欢使用AFTER INSERT触发器(或用于INSERT操作的存储过程)仅计算一次并存储该日期。

另外,提醒一下:只有带有公式的计算列每次访问时才会被计算 - 请注意这一点。也就是说,除非指定了PERSISTED关键字,在这种情况下,结果将与其他数据一起存储在行中,这将更适合 - 再次说明,该值一旦计算,就不会再次更改。


谢谢您的回复,因此更好地使用计算列可能是在数据不断变化的情况下。我有一个里程碑成就统计表,各种达成的成就会增加一个人的“分数”。这听起来像是计算列更合适的用法,对吗? - Brian
@Brian:是的,就像那样。或者如果您有一个可能随时间变化的日期,并且对于某些查询,您需要按该日期的月份和年份进行选择或分组-那么为该日期计算(并持久化!)的月份和年份将是完美的,因为您也可以轻松地索引这些持久化的计算列。 - marc_s
我遇到了需要使用大于默认值公式128个字符限制的情况。Aaron使公式变得更短,因此在这个特定的例子中不是问题。由于问题涉及最佳实践,我想指出这个限制。对于较长的公式,解决方法可能是使用AFTER INSERT触发器。 - Thronk

11

如果您想延长某人的试用期而不必更改他们的申请日期,则计算列不是正确的选择。为什么不为两个列都使用默认约束条件呢?

USE tempdb;
GO

CREATE TABLE dbo.foo
(
  MemberID INT IDENTITY(1,1),
  JoinDate DATE NOT NULL DEFAULT SYSDATETIME(),
  ProbationEndDate NOT NULL DEFAULT 
    DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH,0,SYSDATETIME())+3, 0))
);

INSERT dbo.foo DEFAULT VALUES;

SELECT MemberID, JoinDate, ProbationEndDate FROM dbo.foo;

结果:

MemberID    JoinDate      ProbationEndDate
--------    ----------    ----------------
1           2013-04-05    2013-06-30

(请注意,我使用了一种稍微简单的方法来获取两个月后的月底。)


3
我喜欢那个用于月末的方法,以后会使用它 :) - Brian

0

插入数据时没有开销;只有在读取列时,才会为该列计算值。因此,我认为您的方法是正确的。


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