更改表格列后应如何更新功能? [重复]

问题描述 投票:0回答:1

假设我有一个包含 2 列的表格:

column1
column2

现在,我根据表格创建一个函数,如下所示:

CREATE OR ALTER Function [dbo].[Fn_Test] ()
RETURNS TABLE
AS
    RETURN
        SELECT *
        FROM Table1

如果稍后我向表中添加第三列或删除其中一列,我的函数不会更新,我必须再次更改我的函数。如何一次性更新所有函数,或者有没有办法在表发生更改后更新与该表相关的函数?

sql sql-server function
1个回答
2
投票

如果稍后我们向表中添加第三列或删除其中一列,我的函数不会更新,我必须再次更改我的函数。

是的。

如何一次性更新所有函数,或者有没有办法在表更改后更新与该表相关的函数?

您需要携带自己的代码生成工具,最好是在 SSDT (

*.sqlproj
) 项目中,因为类似的事情应该受到源代码控制,并且相关的是:没有人应该制作任何临时的工具对生产/实时数据库中的数据库对象(如过程、函数、视图等)进行编辑,最后因为通过使用 SSDT(或类似的工具,如 SQLAlchemy)意味着您可以使用 T4 之类的东西自动生成
SELECT <column-list>
(对于视图/函数/过程)直接来自您的
CREATE TABLE
定义。

...但是这一切都不是自动的,也不是由 MSSQL Server 为您管理的。当您使用 SSDT 时,您的更改将在单独的发布过程中或通过架构比较功能应用。


在您的情况下,您希望

FUNCTION
TABLE
保持同步,那么您需要这样的东西:

MyFunction.sql.tt(这是一个T4模板)

在 T4 模板的

<#+
(模板
class
定义块)中,您可以使用 EnvDTE 和/或
SqlCodeAnalysisRule
来获取
CREATE TABLE
定义并重新生成函数定义:

这是不久前的一篇文章,但我认为仍然相关,它使用 EnvDTE 来获取

CREATE TABLE
定义,我将其用作下面 T4 模板的基础。 另请参阅此质量检查

注意: 我还没有更新 2014 年文章中的 Visual Studio COM 互操作参考,因此它们对于今天的 VS2022 来说已经过时了,但修复这个问题很简单,所以留作练习致读者:

<#@ template language="C#" debug="true" hostspecific="true" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #>
<#@ assembly name="VSLangProj" #>
<#@ assembly name="C:\Program Files (x86)\Microsoft SQL Server\120\SDK\Assemblies\Microsoft.SqlServer.TransactSql.ScriptDom.dll" #>
<#@ assembly name="C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\120\Microsoft.SqlServer.Dac.dll" #>
<#@ assembly name="C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\Microsoft\SQLDB\DAC\120\Microsoft.SqlServer.Dac.Extensions.dll" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="EnvDTE80" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ import namespace="Microsoft.SqlServer.Dac" #>
<#@ import namespace="Microsoft.SqlServer.Dac.Model" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".sql" #>
<#
    using TSqlModel model = this.CreateTSqlModel();

    TSqlTable myTable = RequireTable( model, "MyTable" );
 
#>

CREATE FUNCTION dbo.MyFunction() RETURNS TABLE
    WITH SCHEMABINDING
AS RETURN

SELECT
<#    foreach( Column c in myTable.Columns ) { /* See https://learn.microsoft.com/en-us/dotnet/api/microsoft.sqlserver.dac.model.column?view=sql-dacfx-162 */ #>
    t.<#= c.Name #>,
<#    } // foreach Column #>
FROM
    dbo.MyTable;

END;

<#+
 
    public TSqlModel CreateTSqlModel()
    {
        IServiceProvider hostServiceProvider = (IServiceProvider)this.Host;
        DTE              dte                 = (DTE)hostServiceProvider.GetService( typeof(DTE) );

        TSqlModel model = new TSqlModel(SqlServerVersion.Sql110, new TSqlModelOptions { });

        IReadOnlyList<ProjectItem> tableItems = dte.Solution.Cast<Project>()
            .SelectMany( p => p.ProjectItems )
            .Where( i => i.Name.EndsWith( ".sql", StringComparison.OrdinalIgnoreCase ) )
            .Where( i => i.FileCount == 1 && i.FileNames[0].Contains( "Tables" ) ) )
            .ToList();

        foreach( ProjectItem tableItem in tableItems )
        {
            String sqlText = File.ReadAllText( tableItem.FileNames[0] );
            if( sqlText.Contains( "CREATE TABLE" ) )
            {
                model.AddObjects( sqlText );
            }
        }

        return model;
    }

    public static TSqlTable RequireTable( TSqlModel model, String tableName )
    {
        return model.GetObjects<TSqlTable>( DacQueryScopes.All, ModelSchema.Table )
            .Cast<TSqlTable>()
            .Where( t => t.Name == tableName )
            .Single();
    }

#>
© www.soinside.com 2019 - 2024. All rights reserved.