SQL 服务器使用计算列和用户定义的函数根据另一列的变化获取日期时间
SQL server using computed column and user defined function to grab datetime based on change in another column
给定:给定一个 Microsoft SQL(2016 年及以上)数据库 table Log
,其中包含多个列,包括以下重要列:id
(主键),code
(一个可以取多个值表示状态变化的整数),lastupdated
(日期时间字段)...
我需要:
我需要添加一个计算列 ActiveDate
来存储代码更改为 10(即活动状态)的第一次准确时间。随着状态在未来不断变化,此列必须保持与它激活的确切时间相同的值(从而持久保持活动日期时间记录)。此时间戳值最初应以 NULL 开头。
我的做法
我希望 activedate 字段自动存储状态码变为 10 的日期时间,但是当状态再次发生变化时,我希望它保持不变。由于我无法从计算列中引用计算列,因此我创建了一个用户定义的函数来获取 activedate 的当前值,并在状态代码不是 10 时使用它。
限制:
- 我无法修改 Db 或列(除了我可以添加的新列)。
- 此 T-SQL 脚本必须是幂等的,以便它可以在生产管道中随时 运行 多次而不会丢失或损坏数据。
这是我试过的。
IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE Name=N'ActiveDate' AND OBJECT_ID = OBJECT_ID(N'[dbo].[Log]'))
/* First, create a dummy ActiveDate column since the user-defined function below needs it */
ALTER TABLE [dbo].[Log] ADD ActiveDate DATETIME NULL
IF OBJECT_ID('UDF_GetActiveDate', 'FN') IS NOT NULL
DROP FUNCTION UDF_GetActiveDate
GO
/* Function to grab the datetime when status goes active, otherwise leave it unchanged */
CREATE FUNCTION UDF_GetActiveDate(@ID INT, @code INT) RETURNS DATETIME WITH SCHEMABINDING AS
BEGIN
DECLARE @statusDate DATETIME
SELECT @statusDate = CASE
WHEN (@code = 10) THEN [lastupdated]
ELSE (SELECT [ActiveDate] from [dbo].[Log] WHERE id=@ID)
END
FROM [dbo].[Log] WHERE id=@ID
RETURN @statusDate
END
GO
/* Rename the dummy ActiveDate column so that we can be allowed to create the computed one */
EXEC sp_rename '[dbo].[Log].ActiveDate', 'ActiveDateTemp', 'COLUMN';
/* Computed column for ActiveDate */
ALTER TABLE [dbo].[Log] ADD ActiveDate AS (
[dbo].UDF_GetActiveDate([id],[code])
) PERSISTED NOT NULL
/* Delete the dummy ActiveDate column */
ALTER TABLE [dbo].[Log] DROP COLUMN ActiveDateTemp;
print ('Successfully added ActiveDate column to Log table')
GO
我得到的:以下错误
- [dbo].[Log].ActiveDate 无法重命名,因为对象
参与强制依赖。
- 每个 table 中的列名称
必须是唯一的。 table 'dbo.Log' 中的列名 'ActiveDate' 是
指定了不止一次。
我的做法有错吗?还是有更好的方法来达到相同的结果?请帮忙。
您不应尝试从自身计算列。
相反,我会使用触发器...
CREATE TRIGGER dbo.log__set_active_date
ON dbo.log
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE
log
SET
active_date = INSERTED.last_updated
FROM
dbo.log
INNER JOIN
INSERTED
ON log.id = INSERTED.id
WHERE
INSERTED.code = 10
AND log.active_date IS NULL -- Added to ensure the value is only ever copied ONCE
END
我建议您不要为此使用计算列或函数。
只需创建一个使用 window 函数的查询:
SELECT
id,
code,
lastupdateddate,
ActiveDate = MIN(CASE WHEN l.code = 10 THEN l.lastupdateddate END)
OVER (PARTITION BY l.id)
FROM dbo.Log;
给定:给定一个 Microsoft SQL(2016 年及以上)数据库 table Log
,其中包含多个列,包括以下重要列:id
(主键),code
(一个可以取多个值表示状态变化的整数),lastupdated
(日期时间字段)...
我需要:
我需要添加一个计算列 ActiveDate
来存储代码更改为 10(即活动状态)的第一次准确时间。随着状态在未来不断变化,此列必须保持与它激活的确切时间相同的值(从而持久保持活动日期时间记录)。此时间戳值最初应以 NULL 开头。
我的做法 我希望 activedate 字段自动存储状态码变为 10 的日期时间,但是当状态再次发生变化时,我希望它保持不变。由于我无法从计算列中引用计算列,因此我创建了一个用户定义的函数来获取 activedate 的当前值,并在状态代码不是 10 时使用它。
限制:
- 我无法修改 Db 或列(除了我可以添加的新列)。
- 此 T-SQL 脚本必须是幂等的,以便它可以在生产管道中随时 运行 多次而不会丢失或损坏数据。
这是我试过的。
IF NOT EXISTS (SELECT 1 FROM sys.columns WHERE Name=N'ActiveDate' AND OBJECT_ID = OBJECT_ID(N'[dbo].[Log]'))
/* First, create a dummy ActiveDate column since the user-defined function below needs it */
ALTER TABLE [dbo].[Log] ADD ActiveDate DATETIME NULL
IF OBJECT_ID('UDF_GetActiveDate', 'FN') IS NOT NULL
DROP FUNCTION UDF_GetActiveDate
GO
/* Function to grab the datetime when status goes active, otherwise leave it unchanged */
CREATE FUNCTION UDF_GetActiveDate(@ID INT, @code INT) RETURNS DATETIME WITH SCHEMABINDING AS
BEGIN
DECLARE @statusDate DATETIME
SELECT @statusDate = CASE
WHEN (@code = 10) THEN [lastupdated]
ELSE (SELECT [ActiveDate] from [dbo].[Log] WHERE id=@ID)
END
FROM [dbo].[Log] WHERE id=@ID
RETURN @statusDate
END
GO
/* Rename the dummy ActiveDate column so that we can be allowed to create the computed one */
EXEC sp_rename '[dbo].[Log].ActiveDate', 'ActiveDateTemp', 'COLUMN';
/* Computed column for ActiveDate */
ALTER TABLE [dbo].[Log] ADD ActiveDate AS (
[dbo].UDF_GetActiveDate([id],[code])
) PERSISTED NOT NULL
/* Delete the dummy ActiveDate column */
ALTER TABLE [dbo].[Log] DROP COLUMN ActiveDateTemp;
print ('Successfully added ActiveDate column to Log table')
GO
我得到的:以下错误
- [dbo].[Log].ActiveDate 无法重命名,因为对象 参与强制依赖。
- 每个 table 中的列名称 必须是唯一的。 table 'dbo.Log' 中的列名 'ActiveDate' 是 指定了不止一次。
我的做法有错吗?还是有更好的方法来达到相同的结果?请帮忙。
您不应尝试从自身计算列。
相反,我会使用触发器...
CREATE TRIGGER dbo.log__set_active_date
ON dbo.log
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
UPDATE
log
SET
active_date = INSERTED.last_updated
FROM
dbo.log
INNER JOIN
INSERTED
ON log.id = INSERTED.id
WHERE
INSERTED.code = 10
AND log.active_date IS NULL -- Added to ensure the value is only ever copied ONCE
END
我建议您不要为此使用计算列或函数。
只需创建一个使用 window 函数的查询:
SELECT
id,
code,
lastupdateddate,
ActiveDate = MIN(CASE WHEN l.code = 10 THEN l.lastupdateddate END)
OVER (PARTITION BY l.id)
FROM dbo.Log;