Sql server 如何在SSDT项目中包括自定义数据迁移和静态/参考数据?

Sql server 如何在SSDT项目中包括自定义数据迁移和静态/参考数据?,sql-server,data-migration,sql-server-data-tools,Sql Server,Data Migration,Sql Server Data Tools,我们有一个中等规模的SSDT项目(~100个表),它部署到几十个不同的数据库实例中。作为构建过程的一部分,我们生成一个.dacpac文件,然后当我们准备升级数据库时,我们生成一个发布脚本并对数据库运行它。有些db实例在不同的时间升级,因此我们有一个结构化的升级和版本控制过程是很重要的 生成的大多数迁移脚本正在删除和(重新)创建过程、函数、索引,并执行任何结构更改,以及部署后脚本中包含的一些数据脚本。我想知道如何在项目中最好地构建这两个与数据相关的项目: 版本之间需要自定义数据迁移 静态或参考数据

我们有一个中等规模的SSDT项目(~100个表),它部署到几十个不同的数据库实例中。作为构建过程的一部分,我们生成一个.dacpac文件,然后当我们准备升级数据库时,我们生成一个发布脚本并对数据库运行它。有些db实例在不同的时间升级,因此我们有一个结构化的升级和版本控制过程是很重要的

生成的大多数迁移脚本正在删除和(重新)创建过程、函数、索引,并执行任何结构更改,以及部署后脚本中包含的一些数据脚本。我想知道如何在项目中最好地构建这两个与数据相关的项目:

  • 版本之间需要自定义数据迁移

  • 静态或参考数据

  • 版本之间需要自定义数据迁移

    有时,我们希望执行一次性数据迁移作为升级的一部分,我不确定将其纳入SSDT项目的最佳方式。例如,最近我添加了一个新的位列
    dbo.Charge.HasComments
    ,以包含基于另一个表的(冗余)派生数据,并将通过触发器保持同步。恼人但必要的性能改进(仅在仔细考虑和测量后添加)。作为升级的一部分,SSDT生成的发布脚本将包含必要的
    ALTER TABLE
    CREATE TRIGGER
    语句,但我还想根据另一个表中的数据更新此列:

    update dbo.Charge 
    set HasComments = 1 
    where exists ( select * 
                   from dbo.ChargeComment 
                   where ChargeComment.ChargeId = Charge.ChargeId ) 
    and HasComments = 0 
    
    在我的SSDT项目中包含此数据迁移脚本的最佳方式是什么

    目前,我在部署后脚本中包含的单独文件中有每种类型的迁移,因此我的部署后脚本最终如下所示:

    -- data migrations
    :r "data migration\Update dbo.Charge.HasComments if never populated.sql"
    go
    :r "data migration\Update some other new table or column.sql"
    go
    
    -- table listing all the correct static data
    declare @working_data table (...)
    
    -- add all the static data that should exist into the working table
    insert into @working_data (...) select null, null null where 1=0
    union all select 'row1 col1 value', 'col2 value', etc...
    union all select 'row2 col1 value', 'col2 value', etc...
    ...
    
    -- insert any missing records in the live table
    insert into staticDataTableX (...)
    select * from @working_data
    where not exists ( select * from staticDataTableX
                       where [... primary key join on @working_data...] )
    
    -- update any columns that should be updated
    update staticDataTableX
    set ...
    from staticDataTableX
    inner join @working_data on [... primary key join on @working_data...]
    
    -- delete any records, if appropriate with this sort of static data
    delete from staticDataTableX
    where not exists ( select * from staticDataTableX
                       where [... primary key join on @working_data...] )
    
    -- static data. each script adds any missing static/reference data:
    :r "static_data\settings.sql"
    go
    :r "static_data\other_static_data.sql"
    go
    :r "static_data\more_static_data.sql"
    go
    
    这是正确的方法吗?或者是否有某种方法可以更好地与SSDT及其版本跟踪相结合,以便在针对已经是最新版本的数据库运行SSDT发布时,这些脚本甚至不会运行。我可以有自己的表来跟踪运行了哪些迁移,但如果有标准的方法来做这些事情,我不想自己滚动

    静态或参考数据

    一些数据库表包含我们称之为静态或参考数据的数据,例如可能的时区列表、设置类型、货币、各种“类型”表等。目前,我们通过为作为部署后脚本一部分运行的每个表使用单独的脚本来填充这些数据。每个静态数据脚本都将所有“正确”的静态数据插入到表变量中,然后根据需要插入/更新/删除静态数据表。根据表的不同,可能只适合插入或只适合插入和删除,而不适合更新现有记录。因此,每个脚本看起来如下所示:

    -- data migrations
    :r "data migration\Update dbo.Charge.HasComments if never populated.sql"
    go
    :r "data migration\Update some other new table or column.sql"
    go
    
    -- table listing all the correct static data
    declare @working_data table (...)
    
    -- add all the static data that should exist into the working table
    insert into @working_data (...) select null, null null where 1=0
    union all select 'row1 col1 value', 'col2 value', etc...
    union all select 'row2 col1 value', 'col2 value', etc...
    ...
    
    -- insert any missing records in the live table
    insert into staticDataTableX (...)
    select * from @working_data
    where not exists ( select * from staticDataTableX
                       where [... primary key join on @working_data...] )
    
    -- update any columns that should be updated
    update staticDataTableX
    set ...
    from staticDataTableX
    inner join @working_data on [... primary key join on @working_data...]
    
    -- delete any records, if appropriate with this sort of static data
    delete from staticDataTableX
    where not exists ( select * from staticDataTableX
                       where [... primary key join on @working_data...] )
    
    -- static data. each script adds any missing static/reference data:
    :r "static_data\settings.sql"
    go
    :r "static_data\other_static_data.sql"
    go
    :r "static_data\more_static_data.sql"
    go
    
    然后,我的部署后脚本有这样一个部分:

    -- data migrations
    :r "data migration\Update dbo.Charge.HasComments if never populated.sql"
    go
    :r "data migration\Update some other new table or column.sql"
    go
    
    -- table listing all the correct static data
    declare @working_data table (...)
    
    -- add all the static data that should exist into the working table
    insert into @working_data (...) select null, null null where 1=0
    union all select 'row1 col1 value', 'col2 value', etc...
    union all select 'row2 col1 value', 'col2 value', etc...
    ...
    
    -- insert any missing records in the live table
    insert into staticDataTableX (...)
    select * from @working_data
    where not exists ( select * from staticDataTableX
                       where [... primary key join on @working_data...] )
    
    -- update any columns that should be updated
    update staticDataTableX
    set ...
    from staticDataTableX
    inner join @working_data on [... primary key join on @working_data...]
    
    -- delete any records, if appropriate with this sort of static data
    delete from staticDataTableX
    where not exists ( select * from staticDataTableX
                       where [... primary key join on @working_data...] )
    
    -- static data. each script adds any missing static/reference data:
    :r "static_data\settings.sql"
    go
    :r "static_data\other_static_data.sql"
    go
    :r "static_data\more_static_data.sql"
    go
    

    作为SSDT项目的一部分,是否有更好或更传统的方法来构造此类静态数据脚本?

    要跟踪字段是否已初始化,请在执行初始化时尝试添加扩展属性(也可用于确定是否需要初始化):

    要添加扩展属性,请执行以下操作:

    EXEC sys.sp_addextendedproperty 
    @name = N'EP_Charge_HasComments', 
    @value = N'Initialized', 
    @level0type = N'SCHEMA', @level0name = dbo, 
    @level1type = N'TABLE',  @level1name = Charge,
    @level2type = N'COLUMN', @level2name = HasComments;
    
    SELECT objtype, objname, name, value
    FROM fn_listextendedproperty (NULL, 
        'SCHEMA', 'dbo', 
        'TABLE', 'Charge', 
        'COLUMN', 'HasComments');
    
    要检查扩展属性,请执行以下操作:

    EXEC sys.sp_addextendedproperty 
    @name = N'EP_Charge_HasComments', 
    @value = N'Initialized', 
    @level0type = N'SCHEMA', @level0name = dbo, 
    @level1type = N'TABLE',  @level1name = Charge,
    @level2type = N'COLUMN', @level2name = HasComments;
    
    SELECT objtype, objname, name, value
    FROM fn_listextendedproperty (NULL, 
        'SCHEMA', 'dbo', 
        'TABLE', 'Charge', 
        'COLUMN', 'HasComments');
    
    对于引用数据,请尝试使用合并。它比您使用的三重查询集要干净得多

    MERGE INTO staticDataTableX AS Target
    USING (
    VALUES  
            ('row1_UniqueID', 'row1_col1_value', 'col2_value'),
            ('row2_UniqueID', 'row2_col1_value', 'col2_value'),
            ('row3_UniqueID', 'row3_col1_value', 'col2_value'),
            ('row4_UniqueID', 'row4_col1_value', 'col2_value')
    
    
        ) AS Source (TableXID,  col1, col2)
            ON Target.TableXID = Source.TableXID
    WHEN MATCHED THEN
       UPDATE SET 
            Target.col1 = Source.col1,  
            Target.col2 = Source.col2  
    
    WHEN NOT MATCHED BY TARGET THEN
       INSERT (TableXID,  col1, col2)
       VALUES (Source.TableXID,  Source.col1, Source.col2)
    
    WHEN NOT MATCHED BY SOURCE THEN
        DELETE; 
    

    这对我来说似乎是对的。杰米·汤姆森(Jamie Thomson)在他的博客上有一些关于维护一个“版本”类型表的想法,您可以参考它。如果以后不想重复脚本,还可以创建一个表来指示运行了哪些脚本。您需要为它编写代码,但它可以工作。遗憾的是,目前SSDT中的版本号跟踪似乎有点不足。@Rory:你有没有取得任何进展或提出有效的解决方案?我目前的猜测是,在部署后脚本执行期间,看看DACPAC的版本是否可用,然后在每个数据脚本开始时检查版本号。我们继续使用上面描述的模式。我认为我们已经添加了一个表来记录哪些迁移已经运行,为了处理数据迁移,我们只想运行一次,而不能从数据库结构中确定。虽然创建和维护静态数据文件有点麻烦,但我上面描述的模式工作得很好。这适用于DAC数据库吗?我认为DAC/DACPAC不支持扩展道具。