更改计算列引用的 SQL 函数

Posted

技术标签:

【中文标题】更改计算列引用的 SQL 函数【英文标题】:Alter SQL Function Referenced by Computed Column 【发布时间】:2010-10-07 07:29:29 【问题描述】:

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

您能否告诉 SQL Server 您不关心公式是否引用了函数,而只是继续更改基础函数?

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

【问题讨论】:

我同意这是一个很大的痛苦!马上感受! 我遇到了同样的问题。我认为 MS SQL 不允许这样做是有充分理由的,但它仍然是一个真正的痛苦(+1 表示共同悲伤) 【参考方案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

【讨论】:

【参考方案2】:

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

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

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

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

【讨论】:

【参考方案3】:

假设表 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

【讨论】:

【参考方案4】:

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

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

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

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

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

INSERT INTO MyTable SELECT * FROM MyOtherTable WHERE ...

如果将新列添加到一个表而不是另一个表,则会失败。对于 Computed Column,我们必须明确命名所有列,这使我们失去了安全网。

【讨论】:

就是这样,计算列的值是不确定的。这意味着如果你问它是什么,它可能会说一件事,然后一秒钟后完全没有数据更改,它可能会说一些不同的东西。触发器在这里不起作用。 我确实读过说是不确定的,然后我的思绪飘了!对此感到抱歉。【参考方案5】:

ALTER 的后果可能是巨大的。

您是否为这些列编制了索引?在带有模式绑定的视图中使用它?坚持了吗?外键有关系吗?

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

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

【讨论】:

函数是时间相关的,不能持久化。出于同样的原因,它不能有 FK 约束(即使列 ALWAYS 恰好是引用另一个表的 PK 的有效值)。 这是你的函数,不是一般情况。更好的是:始终禁止带有依赖项的 ALTER FUNCTION 或几乎随机地允许它? 我的函数没什么特别的,它的行为来自于它的不确定性。我在我的问题中通过编辑澄清了这一点。更改非确定性函数应该不会造成不良影响。但有可能吗?【参考方案6】:

您可以尝试一些好的架构比较工具,为您创建脚本:)

【讨论】:

【参考方案7】:

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

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

马克

【讨论】:

好吧,我想你不能这样做。这就是你说的,你先说的。 真的吗?难道你不能从列中删除约束,然后改变函数,然后重新应用约束吗?

以上是关于更改计算列引用的 SQL 函数的主要内容,如果未能解决你的问题,请参考以下文章

在整个 SQL 数据库中搜索列

SQL Server 引用计算列

如何重命名SQL Server中计算列中引用的列?

如何对sql查询引用组中的多列求和

错误:列xxx引用的值表达式不能在Proc SQL set语句中直接包含汇总函数

查找引用某个表中特定列的所有存储过程