为表和相关数据生成插入语句

Ale*_*ith 5 foreign-key sql-server migration insert

我希望在 2 个数据库之间移动表和相关表中的数据。问题是主键不同步。

有没有人知道可以为给定表生成插入语句及其相关数据(可能是外键)的工具?

我一直在查看 SSMS 工具包、RedGate 的工具、数据库项目数据比较、各种开源数据差异工具等,但没有找到任何东西。

我知道能够遍历外键,所以也许一些非常聪明的 SQL 脚本可能能够做到这一点?

看到我看过这么多工具,我猜想完全自动化可能很难/不可能。但也许一种技术也需要一些人工干预?

或者也许有人对他们如何处理此类任务有任何提示。我真的不喜欢编写大量手动 sql 来处理插入 - 因为它肯定容易出错。

我真正希望的是一种生成 SQL 以在新数据库中进行插入时绑定 PK 和 FK 的方法。

比如说我们有:

+--------------+--------------+
|     personid |     name     |
+--------------+--------------+
|            1 |         bob  |
|            2 |         fred |
+--------------+--------------+

+----------+----------------+----------+
| animalid |     animal     | PersonID |
+----------+----------------+----------+
|        1 |         cat    |        1 |
|        2 |         dog    |        1 |
|        3 |         lizard |        2 |
+----------+----------------+----------+
Run Code Online (Sandbox Code Playgroud)

将 bob 复制到新数据库和 bob 的猫和狗会很棒。然后是弗雷德和弗雷德的蜥蜴。

Kev*_*sel 2

SSMS 工具包允许您为特定表生成插入脚本。在“功能”页面上,它是“从结果集、表或数据库生成插入语句”。但是,这不会自动获取所有子表。

较旧的 Stack Overflow 帖子中,他们提到了一些生成插入的方法。如果您有 SQL 2005 或更高版本,则第一种方法(使用 SSMS 的生成脚本功能)将起作用。如果您使用的是 2000,第二种方法(下载的存储过程)就可以完成这项工作。

假设您有 2005 或更高版本,右键单击数据库并转到任务 --> 生成脚本。从那里,您可以选择特定的数据库对象。您将选择所有必要的表(因此这是手动过程)。

要获取表列表,您对遍历外键列表的看法是正确的。那是在sys.foreign_keys. 以下脚本将从中心点获取所有外键依赖项的列表。请注意,这不会获取子记录,而只会获取“父”记录。例如,假设 T3 具有 T2 的外键 (T2ID),T2 具有 T1 的外键 (T1ID)。如果我们在 T3 上运行此脚本,它将返回三个表:T3、T2 和 T1。如果我们在 T2 上运行它,它将返回两个表:T2 和 T1。如果我们在 T1 上运行它,它将仅返回 T1。

declare @SchemaName sysname = 'dbo';
declare @TableName sysname = 't2';

with foreignkeys as
(
    select
        tbl.object_id as ObjectID,
        tbls.name as SchemaName,
        tbl.name as TableName,
        convert(varchar(8000), tbls.name + '.' + tbl.name) as [Path]
    from 
        sys.foreign_keys fk
        inner join sys.tables tbl on fk.parent_object_id = tbl.object_id
        inner join sys.schemas tbls on tbl.schema_id = tbls.schema_id
    where
        tbls.name = @SchemaName
        and tbl.name = @TableName

    UNION ALL

    select
        ref.object_id as ObjectID,
        refs.name as SchemaName,
        ref.name as TableName,
        convert(varchar(8000), fks.[Path] + '/' + refs.name + '.' + ref.name) as [Path]
    from
        sys.foreign_keys fk
        inner join sys.tables ref on fk.referenced_object_id = ref.object_id
        inner join sys.schemas refs on ref.schema_id = refs.schema_id
        inner join foreignkeys fks on fks.objectid = fk.parent_object_id
    where
        fks.objectid <> ref.object_id       
        AND fks.[Path] NOT LIKE '%' + refs.name + '.' + ref.name + '%'
)
select 
    SchemaName + '.' + TableName
from 
    foreignkeys

UNION

select @SchemaName + '.' + @TableName;
Run Code Online (Sandbox Code Playgroud)

如果您还想获取子表(即获取整个外键依赖链),另一个 CTE 将执行此操作:

declare @SchemaName sysname = 'dbo';
declare @TableName sysname = 't2';

with foreignkeys as
(
    select
        tbl.object_id as ObjectID,
        tbls.name as SchemaName,
        tbl.name as TableName,
        convert(varchar(8000), tbls.name + '.' + tbl.name) as [Path]
    from 
        sys.foreign_keys fk
        inner join sys.tables tbl on fk.parent_object_id = tbl.object_id
        inner join sys.schemas tbls on tbl.schema_id = tbls.schema_id
    where
        tbls.name = @SchemaName
        and tbl.name = @TableName

    UNION ALL

    select
        ref.object_id as ObjectID,
        refs.name as SchemaName,
        ref.name as TableName,
        convert(varchar(8000), fks.[Path] + '/' + refs.name + '.' + ref.name) as [Path]
    from
        sys.foreign_keys fk
        inner join sys.tables ref on fk.referenced_object_id = ref.object_id
        inner join sys.schemas refs on ref.schema_id = refs.schema_id
        inner join foreignkeys fks on fks.objectid = fk.parent_object_id
    where
        fks.objectid <> ref.object_id       
        AND fks.[Path] NOT LIKE '%' + refs.name + '.' + ref.name + '%'
),
parentsandchildren as
(
    select
        ObjectID,
        SchemaName,
        TableName
    from
        foreignkeys

    UNION ALL

    select 
        tbl.object_id as ObjectID,
        tbls.name as SchemaName,
        tbl.name as TableName
    from
        sys.foreign_keys fk
        inner join sys.tables tbl on fk.parent_object_id = tbl.object_id
        inner join sys.schemas tbls on tbl.schema_id = tbls.schema_id
        inner join parentsandchildren fks on fks.objectid = fk.referenced_object_id
    where
        not exists (select * from foreignkeys fk where fk.objectid = tbl.object_id)

)
select 
    SchemaName + '.' + TableName
from 
    parentsandchildren

UNION

select @SchemaName + '.' + @TableName;
Run Code Online (Sandbox Code Playgroud)

如果您在 T1、T2 或 T3 上运行此脚本,它将返回所有三个表的列表(因为它在两个方向上遍历链)。如果您的表结构具有其中任何一个,这两个脚本也足够智能,不会陷入自连接和循环中。

运行这两个脚本之一将获得表列表,然后您可以在“生成脚本”菜单中选择这些表。只需确保将脚本数据类型选项设置为架构和数据,否则您将只能获得表结构。

编辑

我不知道有什么工具可以按照您要求的方式编写单独的记录。即使这样写,乍一看也很困难,因为您需要知道需要插入哪些记录、依赖于该记录的所有记录、依赖于这些记录的所有记录等等。这本身就是一个相当简单的递归问题。但是,您需要维护一个结构来告诉何时插入每一行,这样您就不会重复插入记录。对于任意表结构(尤其是带有循环的表结构),这成为一个挑战。

但严格根据你的例子,我不确定做你想做的关于专门插入的事情是否真的有任何价值。就 SQL Server 而言,这两种情况没有区别:

场景一:

  1. 插入 Bob 的人员记录
  2. 插入与鲍勃相关的动物
  3. 插入 Fred 的人员记录
  4. 插入与 Fred 相关的动物

场景B:

  1. 插入 Bob 和 Fred 的人员记录
  2. 插入与 Bob 和 Fred 相关的所有动物记录

插入完成后,无论您选择哪条路径,所有 select 语句都将返回相同的结果。即使您有一个触发器或其他一些依赖于按特定顺序插入的复杂逻辑,我也很难看到 A 起作用但 B 不起作用的情况。如果我们正在讨论 20 世纪 80 年代的 RDBMS,并且您在文件中使用交错父记录和子记录来提高性能,我可以看到这是有道理的,但这在这里并不适用。

相反,您可能会看到正确的插入顺序:首先插入参考数据表,然后沿着链继续。在您的示例中,这是插入到 Person 中,然后插入到 Animal 中。这样,您就不会出现任何外键约束违规,并且所有记录都将是最新的。或者,使用Atlantis Interactive 的 Data Inspector等工具,您可以让它在加载期间禁用外键,然后在之后重新启用它们。