是否有一种解决方法允许在自己的公式中使用计算列

时间:2011-08-24 21:36:40

标签: sql-server-2005 tsql calculated-columns

我的表中有一个名为Value1的列。然后我有一个计算列,Value2,公式为;

(CASE WHEN [Value1] > [Value2] THEN [Value1] ELSE [Value2] END)

我不能保存这个,因为SQL Server在公式中的Value2计算列的自引用时很容易。

我还能做些什么吗?

2 个答案:

答案 0 :(得分:5)

您似乎不仅需要跟踪现在的值1,还要记录曾经的值1。您将无法使用计算列执行此操作,因为它只能对当前值做出反应,而不是对自身或先前的值做出反应。

我建议使用INSTEAD OF TRIGGER而不是计算列。这是一个简单的例子:

USE tempdb;
GO

CREATE TABLE dbo.SparkyMark
(
    [key]    INT IDENTITY(1,1) PRIMARY KEY,
    [string] VARCHAR(32),
    Value1   INT,
    Value2   INT
);
GO

INSTEAD OF INSERT TRIGGER

CREATE TRIGGER dbo.SparkyMark_BeforeInsert
ON dbo.SparkyMark
INSTEAD OF INSERT
AS
BEGIN
    SET NOCOUNT ON;

    INSERT dbo.SparkyMark([string], Value1, Value2)
        SELECT [string], Value1, Value1 FROM inserted;
END
GO

INSTEAD OF UPDATE TRIGGER

CREATE TRIGGER dbo.SparkyMark_BeforeUpdate
ON dbo.SparkyMark
INSTEAD OF UPDATE
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE sm
        SET [string] = i.[string],
            Value1 = i.Value1, 
            Value2 = CASE WHEN sm.Value2 < i.Value1 THEN i.Value1 ELSE sm.Value2 END
        FROM
            dbo.SparkyMark AS sm
        INNER JOIN
            inserted AS i
            ON sm.[key] = i.[key];
END
GO

现在让我们插入几行并证明我们可以维护Value2,而无需直接插入或更新该列:

INSERT dbo.SparkyMark([string], Value1) SELECT 'foo', 3;
INSERT dbo.SparkyMark([string], Value1) SELECT 'foo', 5;

-- Value1 and Value2 are the same:
SELECT * FROM dbo.SparkyMark ORDER BY [key];

-- they will still be the same because the new Value1 > old Value2:
UPDATE dbo.SparkyMark SET Value1 = Value1 + 1;
SELECT * FROM dbo.SparkyMark ORDER BY [key];

-- now they will be one less because the new Value1 < old Value2:
UPDATE dbo.SparkyMark SET Value1 = Value1 - 1;
SELECT * FROM dbo.SparkyMark ORDER BY [key];

-- in row 1 Value1 drops by 2 but Value2 stays the same:
UPDATE dbo.SparkyMark SET Value1 = Value1 - 2 WHERE [key] = 1;
SELECT * FROM dbo.SparkyMark ORDER BY [key];

-- and finally we get both values in both rows equal again:
UPDATE dbo.SparkyMark SET Value1 = Value1 + 5;
SELECT * FROM dbo.SparkyMark ORDER BY [key];

清理:

DROP TRIGGER dbo.SparkyMark_BeforeInsert, dbo.SparkyMark_BeforeUpdate;
DROP TABLE dbo.SparkyMark;
GO

答案 1 :(得分:2)

计算列仅在记录为SELECTED时(非PERSISTED)或计算所依赖的某个字段(对于PERSISTED)进行计算。

因此,一种可以提供相同功能(没有触发器)的方法是重新处理存储过程。更新可能会像这样......

ECLARE @myVar varchar(max)
UPDATE dbo.myTable
SET [value1] = 3,
    [value2] = CASE WHEN [value2] < 3 THEN 3 ELSE [value2] END
WHERE ...

它不像触发器那样自动,但是如果你可以避免那些 - 它们通常有点像工作,当涉及到修改你的表,维护,升级等。你必须确保他们能够优雅地失败,以防万一。