Population of Visual Studio Database Project data-loading scripts from existing data

一世执手 提交于 2019-12-12 17:14:12

问题


So, I've been hunting for a solution for this for awhile and have come up with what sort of works ... but I can't help feeling that there must be something more elegant.

What I'm looking for is to be able to extract existing data from a populated database & incorporate that data into loading scripts. The database schema & configuration data will be rolled out multiple times, and will change as development continues, so it's important to be able to rebuild the configuration data from existing data, rather than from static data kept in scripts.

Here's what I've cobbled together:

create procedure #dump (
    @TableName      varchar(128)
    )
as

set nocount on
set rowcount 0

declare @template varchar(max)
set @template = 'SET IDENTITY_INSERT [dbo].[' + @TableName + '] ON

MERGE INTO [dbo].[' + @TableName + '] AS [Target]
USING
(
VALUES
{vals}
)
AS [Source] ({fields})
ON [Target].[{pk}] = [Source].[{pk}]
WHEN MATCHED THEN UPDATE SET
{upds}
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
{fields}
)
VALUES
(
{fields}
);

SET IDENTITY_INSERT [dbo].[' + @TableName + '] OFF
--------------------------------------------------
'

declare @pk varchar(max) = ''
declare @vals varchar(max) = '/*
set concat_null_yields_null off
select ''' + '(' + ''' + replace(replace({casts} + '') ,'', '',,'', '', null,''), ''' + ',)' + ''', ''' + ',null)' + ''') from [' + @TableName + ']
*/'
declare @casts varchar(max) = ''
declare @fields varchar(max) = ''
declare @upds varchar(max) = ''
declare @inserts varchar(max) = ''

set @pk = SUBSTRING(@TableName, 1, len(@TableName) - 1) + 'ID'

declare cur_flds
cursor for select c.name, c.type
from syscolumns c
where c.id = object_id(@TableName)
order by c.colid

declare @fn varchar(max)
declare @ft int

open cur_flds
fetch next from cur_flds into @fn, @ft
while @@FETCH_STATUS = 0
    begin
        if len(@fields) > 0
            set @fields = @fields + ', '
        set @fields = @fields + '[' + @fn + ']'

        if len(@casts) > 0
            set @casts = @casts + ' + ' + ''','' + '
        if @ft in(56,55,50,38,48)
            set @casts = @casts + 'cast([' + @fn + '] as varchar)'
        else if @ft = 111
            set @casts = @casts + ''''''''' + ' + 'cast([' + @fn + '] as varchar) + ' + ''''''''''
        else
            set @casts = @casts + ''''''''' + ' + 'replace([' + @fn + '], ''''''''' + ', ' + ''''''''''''') + '''''''''


        if @fn != @pk
            begin
                if len(@upds) > 0
                    set @upds = @upds + ', '
                set @upds = @upds + '[Target].[' + @fn + '] = [Source].[' + @fn + ']'
            end

        fetch next from cur_flds into @fn, @ft
    end

close cur_flds
deallocate cur_flds

set @vals = REPLACE(@vals, '{casts}', @casts)

set @template = REPLACE(@template, '{pk}', @pk)
set @template = REPLACE(@template, '{vals}', @vals)
set @template = REPLACE(@template, '{fields}', @fields)
set @template = REPLACE(@template, '{upds}', @upds)
set @template = REPLACE(@template, '{inserts}', @inserts)

print @template

go


exec #dump 'ActionItemSystems'

drop proc #dump

That ends up giving me output of:

SET IDENTITY_INSERT [dbo].[ActionItemSystems] ON

MERGE INTO [dbo].[ActionItemSystems] AS [Target]
USING
(
VALUES
/*
set concat_null_yields_null off
select '(' + replace(replace(cast([ActionItemSystemID] as varchar) + ',' + '''' + replace([ActionItemSystemName], '''', '''''') + '''' + ') ,', ',,', ', null,'), ',)', ',null)') from [ActionItemSystems]
*/
)
AS [Source] ([ActionItemSystemID], [ActionItemSystemName])
ON [Target].[ActionItemSystemID] = [Source].[ActionItemSystemID]
WHEN MATCHED THEN UPDATE SET
[Target].[ActionItemSystemName] = [Source].[ActionItemSystemName]
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
[ActionItemSystemID], [ActionItemSystemName]
)
VALUES
(
[ActionItemSystemID], [ActionItemSystemName]
);

SET IDENTITY_INSERT [dbo].[ActionItemSystems] OFF

From this point, I can take the commented-out bit

set concat_null_yields_null off
select '(' + replace(replace(cast([ActionItemSystemID] as varchar) + ',' + '''' + replace([ActionItemSystemName], '''', '''''') + '''' + ') ,', ',,', ', null,'), ',)', ',null)') from [ActionItemSystems]

execute that, and get output like:

(33,'7111-5 -Upstream/Seed Lab') ,
(32,'7301-Seed Lab') ,
(30,'7807 UFDF') ,
(14,'BAS Panel Upgrade') ,
(1,'Clean Steam') ,
(13,'DCS') ,
(2,'HWFI') ,
(3,'MCS') ,
(12,'MES') ,
(31,'Seed Lab') ,
(18,'UCS WRO') ,
(34,'Upstream Seed Lab') ,
(29,'Viral Filtration') ,

which can then be incorporated (sans the final comma) into the script.

Now, this solution functions, but it's fragile. It depends on various assumptions (e.g., that the Table Name will have a Primary Key of Table Name - trailing 's' and plus ID) that may not hold true for every solution. It also requires cutting & pasting, and rerunning from the start when the table structures change.

This is probably quite a lot of background ... which I'm partly sharing because I couldn't find anything similar out there & thought that somebody might benefit from this. However, I still come back to my real question, which is to say: where's the tool to generate this kind of script for VS Database Projects? There really should be something - something that would take into account whatever the primary key is, that would generate the thing entire, etc.


回答1:


You can try with this procedure for generating MERGE statements: https://github.com/readyroll/generate-sql-merge It is more advanced version of what you already have.



来源:https://stackoverflow.com/questions/29989065/population-of-visual-studio-database-project-data-loading-scripts-from-existing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!