9

如果将表的列设置为计算列,其公式调用函数,则更改底层函数会变得很痛苦。每次更改时,您都必须找到其公式引用函数的每一列,删除引用,保存表,更改函数,添加所有内容,然后再次保存。即使是很小的变化也是噩梦。

你能告诉 SQL Server 你不关心公式是否引用了函数,而是继续更改底层函数吗?

附加详细信息:计算列不被 FK 约束持久化或引用,因为它是不确定的。该函数考虑了当前时间。它处理的是记录是否过期的问题。

4

7 回答 7

6

不,据我所知,您不能这样做 - 您必须首先删除所有引用函数的计算列,更改函数,然后重新创建计算列。

也许 MS 会在 SQL Server 2010/2011 中给我们一个“CREATE OR ALTER FUNCTION”命令?:-)

马克

于 2009-02-12T22:12:42.207 回答
4

ALTER 的后果可能是巨大的。

您是否对列进行了索引?在带有模式绑定的视图中使用它?坚持了吗?与外键有关系吗?

如果 ALTER 更改了数据类型、可空性或确定性怎么办?

用依赖关系停止 ALTER FUNCTION 比处理这么多场景更容易。

于 2009-02-14T17:02:45.837 回答
3

很抱歉这个迟到的答案,但它可能很有用。

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

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

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

于 2015-01-07T20:19:46.703 回答
1

假设表 T1 包含 C1、C2、C3、C4、C5 列,其中 C4 是计算列

还假设您希望将其替换为 NewFunc 的 C4 引用函数 OldFunc

首先,将所有行中的非计算列移动到临时表

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
于 2011-04-28T14:07:40.610 回答
1

我知道聚会迟到了,但我今天遇到了同样的问题,没有找到任何真正解决问题的方法,所以我迅速编写了一个脚本。

本质上,它使用该函数创建一个临时表,其中包含每个计算列的列信息,从表中删除列。然后,您更新您的函数并让它使用它们的定义再次重新创建所有列。

如果您必须更改定义中的参数(就像我需要的那样),您可以简单地将该部分编写到再次创建定义的位置。

如果您在索引或其他需求中有计算列,则可以轻松扩展代码,但这超出了我的需求范围。

希望它对其他人有用。

/* 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
于 2015-06-23T09:52:01.913 回答
0

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

于 2009-02-13T16:41:22.760 回答
0

您可以将列更改为不计算,并通过 TRIGGER 对其进行更新。

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

编辑:请注意,这可能会将您的 INSERT 插入到原始表名(现在是 VIEW)中。显然,您可以保留旧表,删除计算列,然后创建一个包含计算列的单独 VIEW。

我们必须在计算列上工作足够多次,才能确定它们带来的麻烦多于获得的收益。Fail-saf inserts(1),试图将视图插入到具有计算列的表中,需要弄乱 SET ARITHABORT 等等。

(1) 我们有故障安全插件,例如:

插入 MyTable SELECT * FROM MyOtherTable WHERE ...

如果将新列添加到一个表而不是另一个表,则它们被设计为失败。使用计算列,我们必须明确命名所有列,这使我们失去了安全网。

于 2009-02-16T07:23:33.267 回答