将数据从一个数据库复制到另一个数据库 - 内存不足异常

时间:2017-03-28 07:10:04

标签: c# sql sql-server asp.net-mvc database

我有一项任务,我想将所有数据从一个数据库复制到另一个数据库&跳过2张桌子。有超过200个表。

我已准备好第二个数据库的表结构。

作为解决方案,我创建了一个页面&点击按钮,我有以下代码: -

DataSet ds = new DataSet();
                string connectionString = "Data Source=COMP112\\MSSQLSERVER2014;Initial Catalog=HCMBL;Integrated Security=True;Persist Security Info=True";
                SqlConnection con = new SqlConnection(connectionString);
                //render table name from database
                string sqlTable = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' and TABLE_Schema='" + Session["SchemaName"].ToString() + "' and TABLE_NAME!='ENTRY' and TABLE_NAME!='OT' and TABLE_NAME!='BL_ENTRY' and TABLE_NAME!='BL_OT'";
                con.Open();
                SqlDataAdapter da = new SqlDataAdapter();
                SqlCommand cmd = new SqlCommand(sqlTable, con);
                cmd.CommandType = CommandType.Text;
                da.SelectCommand = cmd;
                da.Fill(ds);
                con.Close();
                //render connection string from WebConfig file
                string strcon = ConfigurationManager.ConnectionStrings["SPSchema"].ConnectionString;

                for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
                {
                    if (!(ds.Tables[0].Rows[i]["TABLE_NAME"].ToString().Contains("Asp")))
                    {
                        string deleteQuery = "Truncate table " + Session["SchemaName"].ToString() + "." + ds.Tables[0].Rows[i]["TABLE_NAME"];
                        con.Open();
                        SqlCommand cmdDelete = new SqlCommand(deleteQuery, con);
                        cmdDelete.ExecuteNonQuery();
                        con.Close();

                        DataSet dataSet = new DataSet();
                        SqlConnection conn = new SqlConnection(strcon);
                        conn.Open();
                        string selectData = "select * from " + Session["SchemaName"].ToString() + "." + ds.Tables[0].Rows[i]["TABLE_NAME"];
                        SqlCommand command = new SqlCommand(selectData, conn);
                        DataTable dataTable = new DataTable();
                        SqlDataAdapter dataAdapter = new SqlDataAdapter(selectData, conn);
                        dataAdapter.FillSchema(dataSet, SchemaType.Mapped);
                        dataAdapter.Fill(dataSet);
                        dataTable = dataSet.Tables[0];
                        conn.Close();

                        if (dataSet.Tables[0].Rows.Count > 0)
                        {
                            //Connect to second Database and Insert row/rows.
                            SqlConnection conn2 = new SqlConnection(connectionString);
                            conn2.Open();
                            SqlBulkCopy bulkCopy = new SqlBulkCopy(conn2);
                            bulkCopy.DestinationTableName = Session["SchemaName"].ToString() + "." + ds.Tables[0].Rows[i]["TABLE_NAME"].ToString();
                            bulkCopy.WriteToServer(dataTable);
                            conn2.Close();
                        }

                    }

                }

当我在少于10个表中插入数据后运行上面的代码时,它会给出内存异常&amp;程序崩溃。

如何处理?我尝试增加SQL Server的内存容量,但仍然是同样的错误。

还有其他方法可以完成任务吗?

3 个答案:

答案 0 :(得分:5)

您所做的与最佳解决方案相去甚远。您正在使用ASP.NET MVC流程将整个数据库的所有数据存入内存,然后将其输出到另一个数据库。如果您的数据库不仅仅是小而重要的,那么这绝对会填满您流程的内存。

这种类型的任务绝不应该通过进程的内存来完成,而是使用某种形式的备份/还原模式。

您应该查看SSIS projects并创建提取,传输和加载(ETL)解决方案,该解决方案可以异步从ASP.NET MVC解决方案中触发。

可以通过以下方式从C#代码触发SSIS解决方案:

var app = new Application();
var package = app.LoadPackage("compiled-package.dtsx", null);
var results = package.Execute();

请参阅此问题以获取更多信息(不是专门关于复制数据库,但有关于从代码触发SSIS包的信息):How to execute an SSIS package from .NET?

<强>替代地

您还可以选择同时对两个数据库运行查询,但这需要一些额外的管道才能完成。 ASP.NET MVC解决方案的用户帐户需要能够访问这两个数据库。如果您的数据库托管在不同的服务器上,您还需要链接一台服务器到另一台服务器:Create linked servers

要直接从insert的输出执行select,请考虑以下事项:

string source = "NAME_OF_SOURCE_DATABASE";
string target = "NAME_OF_TARGET_DATABASE";
string schema = Session["SchemaName"].ToString();
string table = ds.Tables[0].Rows[i]["TABLE_NAME"];

// Uncomment this if you need to deal with autoincrement columns
/*string idInsQuery = $"SET IDENTITY_INSERT {target}.{schema}.{table} ON";
var idInsCommand = new SqlCommand(idInsQuery, conn);
idInsCommand.ExecuteNonQuery();*/

string insQuery = $"INSERT INTO {target}.{schema}.{table} SELECT * FROM {source}.{schema}.{table}";
var insCommand = new SqlCommand(insQuery, conn);
insCommand.ExecuteNonQuery();

// Uncomment this if you need to deal with autoincrement columns
/*string idInsQuery2 = $"SET IDENTITY_INSERT {target}.{schema}.{table} OFF";
var idInsCommand2 = new SqlCommand(idInsQuery2, conn);
idInsCommand2.ExecuteNonQuery();*/

如果表结构相同,这将autoincrement ID或具有默认值的列可能存在问题。

答案 1 :(得分:1)

这会将数据从数据库1中的表复制到数据库2中的表

Insert into db2.dbo.table2 (col1,col2)
Select col1,col2 from db1.dbo.table1

运行此sql语句,数据将被复制而无需往返您的应用程序。

答案 2 :(得分:0)

如果您发现我的方法很有用,请告诉我。 首先,为什么你要写下一个完整的应用程序来完成这项工作,而 SQL Server继承了属性

我的方法是配置链接服务器并配置要复制哪些表而不是哪些表。 https://docs.microsoft.com/en-us/sql/relational-databases/linked-servers/create-linked-servers-sql-server-database-engine

其次,你可以写下简单的存储过程并在你的sql server中安排根据你的日程安排推入另一个服务器数据库。通过这种方式,您可以通过多种方式控制它。我的意思是控制任何依赖项(表级或业务级别)。

要在t-sql中执行此操作,您可以使用以下系统存储过程来安排每日作业。此示例每天凌晨1:00安排。有关各个存储过程的语法和有效参数范围的详细信息,请参阅Microsoft帮助。

DECLARE @job_name NVARCHAR(128), @description NVARCHAR(512), @owner_login_name NVARCHAR(128), @database_name NVARCHAR(128);

SET @job_name = N'Some Title';
SET @description = N'Periodically do something';
SET @owner_login_name = N'login';
SET @database_name = N'Database_Name';

-- Delete job if it already exists:
IF EXISTS(SELECT job_id FROM msdb.dbo.sysjobs WHERE (name = @job_name))
BEGIN
    EXEC msdb.dbo.sp_delete_job
        @job_name = @job_name;
END

-- Create the job:
EXEC  msdb.dbo.sp_add_job
    @job_name=@job_name, 
    @enabled=1, 
    @notify_level_eventlog=0, 
    @notify_level_email=2, 
    @notify_level_netsend=2, 
    @notify_level_page=2, 
    @delete_level=0, 
    @description=@description, 
    @category_name=N'[Uncategorized (Local)]', 
    @owner_login_name=@owner_login_name;

-- Add server:
EXEC msdb.dbo.sp_add_jobserver @job_name=@job_name;

-- Add step to execute SQL:
EXEC msdb.dbo.sp_add_jobstep
    @job_name=@job_name,
    @step_name=N'Execute SQL', 
    @step_id=1, 
    @cmdexec_success_code=0, 
    @on_success_action=1, 
    @on_fail_action=2, 
    @retry_attempts=0, 
    @retry_interval=0, 
    @os_run_priority=0, 
    @subsystem=N'TSQL', 
    @command=N'EXEC my_stored_procedure; -- OR ANY SQL STATEMENT', 
    @database_name=@database_name, 
    @flags=0;

-- Update job to set start step:
EXEC msdb.dbo.sp_update_job
    @job_name=@job_name, 
    @enabled=1, 
    @start_step_id=1, 
    @notify_level_eventlog=0, 
    @notify_level_email=2, 
    @notify_level_netsend=2, 
    @notify_level_page=2, 
    @delete_level=0, 
    @description=@description, 
    @category_name=N'[Uncategorized (Local)]', 
    @owner_login_name=@owner_login_name, 
    @notify_email_operator_name=N'', 
    @notify_netsend_operator_name=N'', 
    @notify_page_operator_name=N'';

-- Schedule job:
EXEC msdb.dbo.sp_add_jobschedule
    @job_name=@job_name,
    @name=N'Daily',
    @enabled=1,
    @freq_type=4,
    @freq_interval=1, 
    @freq_subday_type=1, 
    @freq_subday_interval=0, 
    @freq_relative_interval=0, 
    @freq_recurrence_factor=1, 
    @active_start_date=20170101, --YYYYMMDD
    @active_end_date=99991231, --YYYYMMDD (this represents no end date)
    @active_start_time=010000, --HHMMSS
    @active_end_time=235959; --HHMMSS

如果您需要更多详细信息,请与我们联系。

谢谢, 阿燕