如何使用 insert 语句将数百万不同 RDBMS 的数据插入 SQL Server 数据库?

Posted

技术标签:

【中文标题】如何使用 insert 语句将数百万不同 RDBMS 的数据插入 SQL Server 数据库?【英文标题】:How to insert millions of data of different RDBMS in to SQL Server database with insert statement? 【发布时间】:2018-05-28 14:17:53 【问题描述】:

到目前为止,我的 SQL Server 中有两个数据库,每个数据库都包含一个表。

我有 2 个如下数据库:

1) Db1 (mysql)

2) Db2 (Oracle)

现在我要做的是用来自 MySQL 的 Db1 的数据填充我的 SQL Server db1 数据库表,如下所示:

Insert into Table1 select * from Table1

Select * from Table1(Mysql Db1) - 来自 Mysql 数据库的数据

Insert into Table1(Sql server Db1) - 插入来自 Mysql 的数据 考虑相同架构的数据库

我不想使用 sqlbulk 复制,因为我不想逐块插入数据。考虑到数百万个数据,我想一次性插入所有数据,因为我的操作不仅限于在数据库中插入记录。因此,用户必须等待很长时间,例如首先将数百万个数据逐块插入数据库中,然后再等待我的进一步操作,这也是长时间运行的操作。

所以如果我加快了这个过程,那么考虑到所有记录都在我的 1 个本地 sql 服务器实例中,我可以加快第二次操作。

这可以在 C# 应用程序中实现吗?

更新:我研究了链接服务器,因为 @GorDon Linoff 建议我可以使用链接服务器来实现这种情况,但根据我的研究,我似乎不能通过代码创建链接服务器。

我想在 ado.net 的帮助下做到这一点。

这正是我想要做的:

假设我有 2 个不同的客户端 RDBMS,其中包含 2 个数据库和客户端中的一些表。

所以数据库是这样的:

Sql Server :

Db1

Order
Id      Amount
1       100
2       200
3       300
4       400


Mysql or Oracle :

Db1:

Order
Id      Amount
1       1000
2       2000
3       3000
4       400

现在我想比较源(SQL Server)和目标数据库(MySQL 或 Oracle)的 Amount 列。

我将用于连接这 2 个不同的 RDBMS 数据库表来比较 Amount 列。

在 C# 中,我可以做的是在我的 数据表(内存中) 中逐块获取记录,然后在代码的帮助下比较这些记录,但考虑到数百万条记录,这将花费大量时间.

所以我想做比这更好的事情。

因此我想我在我的本地 SQL 服务器实例中的 2 个数据库中提取这 2 个 RDBMS 记录,然后根据 Id 创建连接这 2 个表的连接查询,然后利用可以比较这数百万的 DBMS 处理能力高效记录。

这样的查询可以有效地比较数百万条记录:

select SqlServer.Id,Mysql.Id,SqlServer.Amount,Mysql.Amount from SqlServerDb.dbo.Order as SqlServer
Left join MysqlDb.dbo.Order as Mysql on SqlServer.Id=Mysql.Id
where SqlServer.Amount != Mysql.Amount

当我的本地服务器实例中有这 2 个不同的 RDBMS 数据时,上述查询有效:SqlServerDb 和 MysqlDb,这将获取以下数量不匹配的记录:

所以我试图将那些记录从源(Sql server Db)获取到其 Amount 列值不匹配的 MySQL。

预期输出:

Id      Amount
1       1000
2       2000
3       3000

那么有什么办法可以实现这个场景呢?

【问题讨论】:

使用链接服务器。 @GordonLinoff 我在项目中提到的链接服务器是否可能? 链接服务器应该可以的。 @GordonLinoff 非常感谢您的建议,但看起来链接服务器仅适用于 Sql 服务器,但如果我将数据库放在 Mysql 或 Oracle 中,它将无法工作。 我不同意你的观点,即通过数据库链接将数据从 oracle 拖到 sqlserver 将比每个数据库的专用批量导出/导入例程更快 【参考方案1】:

SELECT 一侧,使用SELECT ... INTO OUTFILE ... 创建一个.csv 文件(制表符分隔)

INSERT 一侧,使用LOAD DATA INFILE ...(或任何目标机器语法)。

一次完成所有代码可能比分块更容易编码,并且可能(或可能不会)更快地运行。

【讨论】:

所以如果我的表包含数百万条记录,那么这个选择到 Outputfile 将与这个 Insert Load Data File 一起快速吗? 我已经更新了我的问题以显示我到底想要做什么 @Rick James,这是个好主意,但有时它不能正常工作 @Irfan - “不能正常工作”——你能详细说明一下吗? 我已经使用了这种分块执行此操作的方法。但是,如果您使用 DataReader 读取源代码并将读取器传递给 SqlBulkCopy,那么您无需担心数据的大小。我已经使用读取器方法来加载数亿行。使用块方法,您需要仔细规划块的大小,否则您的 .net 进程会占用大量内存。有了数据阅读器,我就不用担心这些了。 【参考方案2】:

SqlBulkCopy 可以接受 DataTableSystem.Data.IDataReader 作为其输入。

使用您的查询读取源数据库,在源 MySQL 或 Oracle DB 上设置 ADO.Net DataReader,并将读取器传递给 SqlBulkCopyWriteToServer() 方法。

这可以无限制地复制几乎任意数量的行。我已经使用数据读取器方法复制了数亿行。

【讨论】:

但是每次我都必须执行此导入过程以比较我们不想执行的 2 个不同 rdbms 数据库表。除此之外还有其他更好的方法吗? 速度非常快。它消除了逐块的方法。我相信逐块加载到 sql server 是您关心的问题。如果不将数据实际导入到同一个 sql server 实例中,您将无法有效地进行比较。我们在 .Net 中构建了额外的框架支持,以实现无缝连接。您可以使用 global temp ## table 轻松加载和比较,因此除了设置 sql 之外不需要任何准备工作。我们已经对流程进行了参数化,因此我们只需要提供源 sql、##temp 表定义和比较查询。 你认为像hadoop这样的大数据可以在这个过程中提供帮助吗?或者一些用户在这个答案中提到的链接服务器? 任何 hadoop 类型的解决方案都需要更多的努力和设置额外的软件。我上面提出的不需要任何额外的设置。特别是如果您使用全局临时表 (##) 作为临时表,您甚至不需要通过您的 DBA。我们已经在企业环境中完成了这项工作,对创建新表、链接服务器等进行了很多控制。您可以通过编写简单的 C# 代码来完成所有这些工作。加载速度仅受源数据库提供数据的能力限制。 SQLBulkCopy 非常快。任何 delta 比较也可以很容易地设计成这个。 赞成您为帮助我所做的努力,但请您详细说明您的最后一句话“任何增量比较也可以轻松设计成这个”【参考方案3】:

如何在远程数据库中添加更改日期。

那么您可以获取自上次同步以来已更改的所有行并进行比较?

【讨论】:

抱歉,这将通过我们的应用程序来完成,我们希望在其中存储此类记录以生成报告并进行一些进一步的操作。【参考方案4】:

首先不要使用链接服务器。这很诱人,但它比它带来的麻烦更多。像更新和插入一样,会将所有目标数据库获取到源数据库,然后进行插入/更新并将所有数据发布到目标。

据我了解,您正在尝试将更改的数据复制到目标系统以获取某些内容。

我建议在源表上使用时间戳列。当源表时间戳列发生任何变化时,sql server 会更新。

在目标上,获取最大 ID 和最大时间戳。最多两个查询。

在源代码中,source.ID <= target.MaxID && source.timestamp >= target.MaxTimeTamp 为 true 的行是上次同步后更改的行(需要更新)。 source.ID > target.MaxID 为 true 的行是上次同步后插入的行。

现在您不必比较两个世界,您只需获得所有更新和插入。

【讨论】:

对不起,这个过程就像1个数据库从1个系统(假设Sql server)迁移到另一个(Mysql)或者可能是Sql Server(Server1)到Sql server(另一个位置的服务器)可能改变一些数据问题,就像我在示例记录中向您展示的那样,3 条记录的金额列如何从源(Sql 服务器)更改为目标(Mysql)。所以现在在我的应用程序的帮助下,我想存储和找出这种类型的有问题的记录,所以有一些操作,比如报告和其他东西 此外,这里有 3 个数据库 invole.2 我想要比较的客户端数据库(任何 Rdbms,如 oracle、Sql server、Mysql 等)和 1 我想要比较的应用程序数据库(严格来说是 Sql Server)从这 2 个数据库中存储有问题的记录,以便在我的应用程序中显示报告 我的观点是,如果您可以保证时间戳列(假设复制为二进制值),则无需比较。仅比较 ID 和时间戳列可以为您提供任何已更改的记录,并且最好的是它不依赖于您在目标上运行的数据库。您的要求是通过比较它们来找到修改过的记录。我只是想在没有比较的情况下达到相同的结果。当我们向客户发布批量数据时,我们正在使用这个解决方案。他们接收插入/更新的数据,并更新他们的数据库。 但是那些将是客户端数据库,我们不能像您所建议的那样说。因此我们必须找到一些其他方法来比较 2 个不同的 rdbms 表。我们正在考虑使用 hadoop hdfs 和 map减少但这里的问题是没有办法在 2 个 hdfs 文件之间创建连接,就像我们如何在 rdbms 中链接 2 个表一样,尽管有一些像 hive 这样的工具允许我们编写 sql 查询,但它们不提供 api 来按需运行这个比较 【参考方案5】:

您需要使用 ODBC 和适当的驱动程序创建链接服务器连接,然后您可以使用 openquery 执行查询。

看看openquery:

https://msdn.microsoft.com/en-us/library/ms188427(v=sql.120).aspx

【讨论】:

但是我已经提到了链接服务器,我可以通过代码创建链接服务器。有可能吗? 我认为您可以使用过程 sp_addlinkedserver (docs.microsoft.com/en-us/sql/relational-databases/…) 创建链接服务器,在此站点中您有一个工作示例 mssqltips.com/sqlservertip/4570/…。 此链接服务器将保留在 MSSQL 服务器上,如果您在运行查询后不再需要它们,则需要删除它们。 但是考虑到 2 个不同的 RDBMS 数据库表比较,您认为 Linkedserver 会帮助我实现预期的输出吗? 是的,它将像普通表一样工作,您可以插入、更新、删除和所有必要的操作。我以前做过。但是要从其他数据库访问表,您将始终需要使用 openquery,例如 SELECT * FROM OPENQUERY(linkedserver_name,"SELECT * FROM TABLE1")【参考方案6】:

是的,SQL Server 在处理集合时非常高效,所以让我们继续发挥它。

简而言之,我要推销的是

    将数据从源加载到目标数据库上的暂存表(暂存表 = 临时保存源表中的原始数据的表,与源表的结构相同...添加跟踪列来品尝)。这将由您的 C# 代码完成...从 source_table 选择到 DataTable,然后选择 SqlBulkCopy 到暂存表。

    在目标数据库上有一个存储过程来协调目标表和暂存表之间的数据。您的 C# 代码调用存储过程。

鉴于您谈论的是数百万行,另一件可以让事情变得更快的事情是在插入到临时表之前删除临时表上的索引,并在插入之后和执行任何选择之前重新创建这些索引。

【讨论】:

但是你不认为这是一种额外的步骤(开销),首先我必须通过逐块数据来执行 sql 批量复制,然后将其存储在 sql server 暂存表中以用于 2 个不同的 rdbms 数据库表比较权 是的,适用于数百/数千行...不适用于数百万行。打个比方……如果我把一副牌放在你厨房的桌子上,另一副放在你的客厅里,然后给你从另一副牌中找到匹配的牌的任务。来回跑 52 次,你会更快完成吗?或者如果你把整个甲板从厨房带到客厅,然后在两个甲板都在同一个地方找到匹配,你会更快吗? 好的,我同意,但我不明白你的第二点。你能告诉我更多关于那个的事情吗 第二点是一个存储过程,其中包含您的查询和修改数据的必要更新语句。

以上是关于如何使用 insert 语句将数百万不同 RDBMS 的数据插入 SQL Server 数据库?的主要内容,如果未能解决你的问题,请参考以下文章

将数百万次写入文件会损坏我的硬盘吗?

高效地将数百万个 JSON 文件附加到单个文件中

将数百万份文档传输到外部硬盘驱动器

哪种语言可以将数百万个脏地址稳定地处理为标准格式?

是否有可读的方法将数百万的数据放入c#代码中?

如何使用 nginx 将数百个主机名重定向到其他主机名