更改计算列引用的SQL函数

10
如果你将一个表的列设置为计算列,其公式调用了一个函数,那么更改该底层函数就会变得非常麻烦。每次更改时,你都必须找到每个引用该函数的公式的列,删除引用,保存表格,修改函数,重新添加所有内容并再次保存。即使是小的更改也会让人崩溃。
你能告诉SQL Server你不关心函数是否被公式引用,并让它去更改底层函数吗?
额外细节:计算列不是持久化的,也没有被FK约束引用,因为它是不确定的。该函数考虑了当前时间。它处理的是记录是否过期的问题。

我同意这是一个很大的痛苦!我现在感受到了它! - Craig Shearer
我有相同的问题。我认为MS SQL不允许这样做是有充分理由的,但仍然很痛苦(+1 分享痛苦)。 - Daniel Brink
7个回答

6

非常抱歉回复晚了,但这个答案可能会有用。

您可以为每个计算列使用一个虚拟函数来调用您的真实函数。

例如:
计算列使用公式:dbo.link_comp('123')
此函数转发参数并调用并返回函数 dbo.link('123')(您的真实函数)
两个函数只需要使用相同的参数并返回相同的类型即可。

然后,被锁定的函数是dbo.link_comp,您仍然可以更改dbo.link。
此外,如果您的函数从其他SQL中调用,则仍然可以使用您的真实函数名称dbo.link,虚拟函数dbo.link_comp仅用于计算列。


6
据我所知,您不能这样做 - 您必须首先删除所有引用函数的计算列,然后更改该函数,最后重新创建计算列。
也许微软会在SQL Server 2010/2011中为我们提供一个“CREATE OR ALTER FUNCTION”命令? :-)
马克

嗯,我想你就是做不到这个。而且这就是你说的话,你先说的。 - colithium
真的吗?你不能只是从列中删除约束,然后修改函数,再重新应用约束吗? - Mr W
现在已经存在"CREATE OR ALTER FUNCTION",但仍然出现相同的错误! - Glen Little

4

ALTER操作的后果可能是巨大的。

您是否已经为列创建索引?在具有模式绑定的视图中使用了它?保存了它?与之有外键关系?

如果ALTER更改了数据类型、NULLability或确定性,会怎样呢?

停止具有依赖关系的ALTER FUNCTION比处理这么多情况要容易得多。


该函数是时间相关的,无法持久化。出于同样的原因,它不能有一个外键约束(即使该列始终是一个有效值,引用另一个表的主键)。 - colithium
这是你的函数,不是一般情况。哪种方式更好:始终禁止具有依赖性的ALTER FUNCTION还是几乎随意允许? - gbn
我的函数没有什么特别之处,它的行为表现在于它是不确定性的。我通过编辑澄清了我的问题。更换非确定性函数不应该造成任何负面影响。但这是否可能呢? - colithium

2

我知道现在有点晚了,但今天我也遇到了同样的问题,而且没有找到任何能够真正解决问题的东西,所以我很快地写了一个脚本。

它基本上创建一个临时表,使用函数来保存每个计算列的列信息,然后从表中删除这些列。然后你可以更新你的函数,并让它再次创建所有的列和它们的定义。

如果你需要更改定义中的参数(就像我需要的那样),你可以简单地将这部分脚本放入重新创建定义的位置。

如果你的索引或其他需求中有计算列,你可以很容易地扩展代码,但这超出了我的需求范围。

希望对其他人有用。

/* Create temporary table to hold definitions */
CREATE TABLE [#FUNCTION]
(
    [TABLE_NAME] nvarchar(255) NOT NULL,
    [COLUMN_NAME] nvarchar(255) NOT NULL,
    [DEFINITION] nvarchar(255) NOT NULL
)
GO

/* Add data to temp table */
INSERT INTO [#FUNCTION] ( [TABLE_NAME], [COLUMN_NAME], [DEFINITION] )
SELECT TABLE_NAME, COLUMN_NAME, definition FROM INFORMATION_SCHEMA.COLUMNS
INNER JOIN sys.computed_columns ON ( object_id = object_id( TABLE_NAME ) AND name = COLUMN_NAME )
WHERE definition LIKE '%MyFunctionName%'
GO

/* Remove columns */
DECLARE @TABLE_NAME nvarchar(255)
DECLARE @COLUMN_NAME nvarchar(255)

DECLARE c_CursorName CURSOR LOCAL FOR SELECT [TABLE_NAME], [COLUMN_NAME] FROM [#FUNCTION]
OPEN c_CursorName

FETCH NEXT FROM c_CursorName INTO @TABLE_NAME, @COLUMN_NAME

WHILE @@FETCH_STATUS = 0
BEGIN
    EXEC( 'ALTER TABLE [' + @TABLE_NAME + '] DROP COLUMN [' + @COLUMN_NAME + ']' )

    FETCH NEXT FROM c_CursorName INTO @TABLE_NAME, @COLUMN_NAME
END

CLOSE c_CursorName
DEALLOCATE c_CursorName
GO

/* Update function */
-- Update function here
GO

/* Recreate computed columns */
DECLARE @TABLE_NAME nvarchar(255)
DECLARE @COLUMN_NAME nvarchar(255)
DECLARE @DEFINITION nvarchar(255)

DECLARE c_CursorName CURSOR LOCAL FOR SELECT [TABLE_NAME], [COLUMN_NAME], [DEFINITION] FROM [#FUNCTION]
OPEN c_CursorName

FETCH NEXT FROM c_CursorName INTO @TABLE_NAME, @COLUMN_NAME, @DEFINITION

WHILE @@FETCH_STATUS = 0
BEGIN
    EXEC( 'ALTER TABLE [' + @TABLE_NAME + '] ADD [' + @COLUMN_NAME + '] AS ' + @DEFINITION )

    FETCH NEXT FROM c_CursorName INTO @TABLE_NAME, @COLUMN_NAME, @DEFINITION
END

CLOSE c_CursorName
DEALLOCATE c_CursorName
GO

/* Remove temp table */
DROP TABLE [#FUNCTION]
GO

2
假设有一个名为T1的表,它包含列C1、C2、C3、C4和C5,其中C4是一个计算列。
另外,假设C4引用了函数OldFunc,你想要将其替换为NewFunc。
首先,将所有行中的非计算列移动到一个临时表中。
Select C1, C2, C3, C5 into TmpT1 from T1
Go

下一步,从T1中删除所有行。
Delete From T1
go

现在您可以修改C4列

Alter table T1 alter column C4 as dbo.NewFunc()
Go

现在将保存的数据放回原始表中。
Insert Into T1 (C1,C2,C3,C5) select C1,C2,C3,C5 from TmpT1

现在删除临时表

Drop Table TmpT1

0
你可以尝试使用一些好的模式比较工具,它可以为你创建脚本 :)

0

你可以将该列更改为非计算列,并通过TRIGGER进行更新。

或者,您可以将表重命名为其他名称,删除计算列,并创建一个视图来代替原始表(即使用原始表名称),并包括您需要的“计算”列。

编辑:请注意,这可能会影响您插入到原始表名称(现在是视图)中的操作。显然,您可以保留旧表,删除计算列,并创建一个单独的视图,其中包含计算列。

我们不得不解决计算列的问题很多次,以至于我们已经决定它们带来的麻烦比收益还要多。安全插入(1),尝试插入到具有计算列的表上的视图,需要处理SET ARITHABORT等问题。

(1)我们有像下面这样的安全插入:

INSERT INTO MyTable SELECT * FROM MyOtherTable WHERE ...

如果在一个表中添加了新列而另一个表没有,则设计为失败。对于计算列,我们必须明确命名所有列,这使我们失去了这个安全网。


这就是问题所在,计算列的值是不确定的。也就是说,如果你问它是什么,它可能会回答一件事情,然后一秒钟后,即使没有任何数据更改,它也可能会回答另一件事情。触发器在这里行不通。 - colithium
我读过说它是非确定性的,然后我的思维就跑偏了!对此我感到抱歉。 - Kristen

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