cod*_*zen 2 sql database insert sql-insert
我有一个数据库模式,它有许多表和表之间的关系 - 你知道,标准的 SQL 数据库设置。
我想生成insert语句以将数据从一个数据库“复制”到另一个具有相同架构但没有数据的数据库。
问题是,如果我以任何顺序执行此操作,它可能无法工作,因为首先插入的数据可能取决于稍后才编写脚本的数据。
如何对insert语句进行排序以使数据依赖项的顺序正确?
您想要的排序称为拓扑排序。这对元素(在您的情况下是表格)进行排序,以便依赖元素出现在依赖元素之后。执行这种排序的一种常用技术是构建图结构并在其上应用排序算法。许多框架都有构建图形的库,并具有为您执行拓扑排序的算法(.Net、Java、Python、C++)。
您将面临的一个问题是您的表是否具有循环关系。例如:
[a] --> [b] --> [a]
这个循环阻止了图被拓扑排序,除非你知道没有一个实体[a]引用实体,[b]而实体又引用了相同的实体[a],你不能确定你会避免引用冲突。
这是 C# 中的示例脚本(使用LinqPad),它查询数据库模式中的关系,使用Quickgraph构建图形,然后对其进行拓扑排序并列出已排序的表(您可以从中构建insert语句),或列出表如果不能按拓扑排序,则使用依赖循环:
http://share.linqpad.net/47qds2.linq
void Main()
{
var targetDb = "MyDb";
var relationSql = @"SELECT pk.TABLE_NAME as PrimaryKeyTable,
fk.TABLE_NAME as ForeignKeyRefTable
FROM
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS r
INNER JOIN
INFORMATION_SCHEMA.TABLE_CONSTRAINTS fk
ON
fk.CONSTRAINT_NAME = r.CONSTRAINT_NAME
INNER JOIN
INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk
ON
pk.CONSTRAINT_NAME = r.UNIQUE_CONSTRAINT_NAME
ORDER BY
ForeignKeyRefTable";
var tableSql = @"select t.[object_id] as TableId, t.[name] as TableName, c.[name] as ColName
from sys.tables t
inner join sys.columns c
on t.object_id = c.object_id";
using(var conn = new SqlConnection(String.Format(@"Data Source=(localdb)\v11.0;Initial Catalog={0}", targetDb)))
{
var tables = conn.Query<Table>(tableSql);
var relations = conn.Query<Relation>(relationSql);
var relationGraph = new QuickGraph.AdjacencyGraph<String, Edge<String>>();
relationGraph.AddVertexRange(tables.Select(t => t.TableName));
var relationEdges = from r in relations
where r.ForeignKeyRefTable != r.PrimaryKeyTable
select new QuickGraph.Edge<String>(r.PrimaryKeyTable, r.ForeignKeyRefTable);
relationGraph.AddEdgeRange(relationEdges);
// The graph can be topologically sorted only if it is acyclic
if (relationGraph.IsDirectedAcyclicGraph())
{
var inRelationOrder = relationGraph.TopologicalSort();
inRelationOrder.Dump("Sorted Tables");
}
else
{
var connected = AlgorithmExtensions.CondensateStronglyConnected<String, Edge<String>, AdjacencyGraph<String, Edge<String>>>(relationGraph);
var cycles = from v in connected.Vertices
where v.VertexCount > 1
select v.Vertices;
cycles.Dump("Dependency Cycles");
}
}
}
public class Table
{
public Int32 TableId { get; set; }
public String TableName { get; set; }
public String ColName{ get; set; }
}
public class Relation
{
public String PrimaryKeyTable { get; set; }
public String ForeignKeyRefTable { get; set; }
}
Run Code Online (Sandbox Code Playgroud)