我正在为一个SQL Server 2012数据库设计一个新的数据库模式。
每个表格应该增加两个额外的列,分别叫做modified
和created
,在行被插入或更新时会自动改变。
我不知道怎样才能最好地实现这一点。
我认为触发器是处理它的最佳方式。
我尝试查找关于触发器的示例...但我找到的教程都将数据插入到另一张表格等操作。
我认为这是一个相当常见的场景,但我还没有找到答案。
我正在为一个SQL Server 2012数据库设计一个新的数据库模式。
每个表格应该增加两个额外的列,分别叫做modified
和created
,在行被插入或更新时会自动改变。
我不知道怎样才能最好地实现这一点。
我认为触发器是处理它的最佳方式。
我尝试查找关于触发器的示例...但我找到的教程都将数据插入到另一张表格等操作。
我认为这是一个相当常见的场景,但我还没有找到答案。
created
列很简单 - 它只是一个DATETIME2(3)
列,并带有一个默认约束,在插入新行时设置:
created
column is simple - just a DATETIME2(3)
column with a default constraint that gets set when a new row is inserted:
Created DATETIME2(3)
CONSTRAINT DF_YourTable_Created DEFAULT (SYSDATETIME())
所以当你向YourTable
插入一行并且没有为Created
指定值时,它将被设置为当前日期和时间。
modified
有点麻烦,因为你需要为AFTER UPDATE
情况编写触发器并更新它 - 你不能声明地告诉SQL Server为你完成此操作....
Modified DATETIME2(3)
然后
CREATE TRIGGER updateModified
ON dbo.YourTable
AFTER UPDATE
AS
UPDATE dbo.YourTable
SET modified = SYSDATETIME()
FROM Inserted i
WHERE dbo.YourTable.PrimaryKey = i.PrimaryKey
你需要加入Inserted
伪表,其中包含使用你的基表的主键更新的所有行。
你需要为每个想要添加modified
列的表创建这个AFTER UPDATE
触发器。
CREATE TABLE [dbo].[Table1]( [created] [datetime2](3) NOT NULL CONSTRAINT [DF_Table1_created] DEFAULT (sysdatetime()), ...)
再加上触发器 CREATE TRIGGER updateModified
即可完成!还有一个提示。表中的触发器名称必须在模式内唯一。CREATE TRIGGER dbo.Table1_updateModified
。干杯,Stefan。 - stevoLastModifiedBy
和CreatedBy
是对users
表(UserID
)的引用,而LastModifiedOn
和CreatedOn
列是日期和时间列。无触发器解决方案 - 有人曾说过:“最好的编写触发器的方法是不编写触发器。” 并且你需要知道,通常它们会影响性能。因此,如果可以避免使用它们,最好这样做,即使在某些情况下,使用触发器可能看起来是最简单的事情。
因此,只需编辑所有的INSERT
和UPDATE
语句,以包含当前的UserID
和当前日期和时间。 如果无法定义此类用户ID
(匿名用户),则可以改用0
,而列的默认值(如果未指定user ID
)将为NULL
。 当插入NULL
值时,您应该找到“问题所在”的语句并进行编辑。
触发器解决方案 - 您可以创建AFTER INSERT, UPDATE
触发器,并在其中填充用户列。 在触发器上下文中获取当前日期和时间很容易(例如使用GETUTCDATE()
)。 这里的问题是触发器不允许传递/接受参数。 因此,由于您没有插入user ID
值,也无法将其传递给触发器。 如何找到当前用户?
您可以使用 SET CONTEXT_INFO 和 CONTEXT_INFO。 在所有insert
和update
语句之前,必须使用SET CONTEXT_INFO
将current user ID
添加到当前上下文中,并在触发器中使用CONTEXT_INFO
函数来提取它。
因此,当使用触发器时,您需要再次编辑所有的INSERT
和UPDATE
语句 - 这就是为什么我更喜欢不使用它们。
无论如何,如果您只需要日期和时间列,而不是创建/修改列,则使用触发器更加耐用和容易,因为您现在和将来都不需要编辑任何其他语句。
使用 SQL Server 2016
,我们现在可以使用SESSION_CONTEXT函数读取会话详细信息。这些详细信息是使用sp_set_session_context设置的(可为只读
或读写
)。这些东西略微用户友好:
EXEC sp_set_session_context 'user_id', 4;
SELECT SESSION_CONTEXT(N'user_id');
一个不错的示例。
LastModifiedBy
和CreatedBy
。我正在使用Adam Schroder的NPoco与存储库模式,并且我很喜欢它!但是我在处理通过代码处理它的方法时有些困难。我是ORM世界的新手。但我会继续努力的 ;) - stevo注意,上述方法在某些情况下可能无法正常工作, 我浪费了很多时间,后来发现以下方法很有用:
create TRIGGER yourtable_update_insert
ON yourtable
AFTER UPDATE
as
begin
set nocount on;
update yourtable set modified=getdate(), modifiedby = suser_sname()
from yourtable t
inner join inserted i on t.uniqueid=i.uniqueid
end
go
set nocount on;
是必需的,否则你会收到以下错误信息:
Microsoft SQL Server Management Studio
未更新任何行。
第5行数据未提交。 错误来源:Microsoft.SqlServer.Management.DataTools。 错误信息:更新或删除的行值要么不唯一,要么修改了多个行(2行)。
请纠正错误后重试,或按ESC取消更改。
确定 帮助
CREATE TRIGGER [dbo].[updateModified]
ON [dbo].[Transaction_details]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Transaction_details
SET ModifedDate = GETDATE() FROM dbo.Transaction_details t JOIN inserted i ON
t.TransactionID = i.TransactionID--SYSDATETIME()
END
alter table Table_Name add CONSTRAINT constraint_name DEFAULT (SYSDATETIME()) for Column_Name
- G.ProtocolALTER TRIGGER [MR3W].[tgUpdateBuilding] ON [MR3W].[vwMrWebBuilding]
INSTEAD OF UPDATE, INSERT AS
BEGIN
SET NOCOUNT ON
IF EXISTS(SELECT * FROM DELETED)
BEGIN
UPDATE [dbo].[Building]
SET
,[BuildingName] = i.BuildingName
,[isActive] = i.isActive
,[updatedAt] = getdate()
FROM dbo.Building b
inner join inserted i on i.BuildingId = b.BuildingId
END
ELSE
BEGIN
INSERT INTO [dbo].[Building]
(
[BuildingName]
,[isActive]
,[updatedAt]
)
SELECT
[BuildingName]
,[isActive]
,getdate()
FROM INSERTED
END
END
这个解决方案可能并不适用于所有的使用情况,但在可能的情况下,它是一种非常干净的方式。 为插入/更新表中的行创建一个存储过程,并且只使用此存储过程来修改表。在存储过程中,您可以随时根据需要设置创建和更新列。例如,设置updatedTime = GetUTCTime()。