从 MySQL 到 MS Access 的 1GB 数据
1GB of Data From MySQL to MS Access
情况: 我正在创建一个自动任务,它查询 MySQL(通过 ODBC)并将结果集插入 MS Access 数据库 (.mdb),使用OLEDB。
代码:
OleDbConnection accCon = new OleDbConnection();
OdbcCommand mySQLCon = new OdbcCommand();
try
{
//connect to mysql
Connect();
mySQLCon.Connection = connection;
//connect to access
accCon.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;" +
@"Data source= " + pathToAccess;
accCon.Open();
var cnt = 0;
while (cnt < 5)
{
if (accCon.State == ConnectionState.Open)
break;
cnt++;
System.Threading.Thread.Sleep(50);
}
if (cnt == 5)
{
ToolBox.logThis("Connection to Access DB did not open. Exit Process");
return;
}
} catch (Exception e)
{
ToolBox.logThis("Faild to Open connections. msg -> " + e.Message + "\n" + e.StackTrace);
}
OleDbCommand accCmn = new OleDbCommand();
accCmn.Connection = accCon;
//access insert query structure
var insertAccessQuery = "INSERT INTO {0} values({1});";
// key = > tbl name in access, value = > mysql query to b executed
foreach (var table in tblNQuery)
{
try
{
mySQLCon.CommandText = table.Value;
//executed mysql query
using (var dataReader = mySQLCon.ExecuteReader())
{
//variable to hold row data
var rowData = new object[dataReader.FieldCount];
var parameters = "";
//read the result set from mysql query
while (dataReader.Read())
{
//fill rowData with the row values
dataReader.GetValues(rowData);
//build the parameters for insert query
for (var i = 0; i < dataReader.FieldCount; i++)
parameters += "'" + rowData[i] + "',";
parameters = parameters.TrimEnd(',');
//insert to access
accCmn.CommandText = string.Format(insertAccessQuery, table.Key, parameters);
try
{
accCmn.ExecuteNonQuery();
}
catch (Exception exc)
{
ToolBox.logThis("Faild to insert to access db. msg -> " + exc.Message + "\n\tInsert query -> " + accCmn.CommandText );
}
parameters = "";
}
}
}
catch (Exception e)
{
ToolBox.logThis("Faild to populate access db. msg -> " + e.Message + "\n" + e.StackTrace);
}
}
Disconnect();
accCmn.Dispose();
accCon.Close();
问题:
内存使用率非常高 (300MB++) 而 MS Access 文件大小却没有经常变化!似乎插入缓存数据而不是将其保存到磁盘。
速度很慢!我知道我的查询会在几秒钟内执行,但插入过程需要很长时间。
我尝试在 MS Access 中使用准备好的语句并将值作为参数而不是字符串连接插入来创建插入查询。但是我收到此异常消息:
Data type mismatch in criteria expression.
有人知道如何解决这个问题或有更好的方法吗?
您可以创建一个 VBA 宏,它使用 DoCmd.TransferDatabase 方法通过 ODBC 将数据提取到您的 Access 数据库中。它可能也会更快更简单。
要运行 来自外部程序或计划任务的 VBA 代码,只需启动 Access 以使用 /x 命令行开关打开您的文件,它将 运行 导入启动时的宏。 1 GB 的数据仍然需要一段时间。我找到了 article by David Catriel that implemented this approach.
更好的选择是使用不同的数据库引擎后端,例如 SQL Server Express 的免费版本。然后你有更多的选择,它也更强大。如果您需要 MS Access 表格和报告,如果您使用 SQL 服务器,则可以创建一个 ADP 项目文件,或者您可以使用链接表来获取数据。如果满足您的要求,您甚至可以 use Access as a front-end to your MySQL database 而不是复制所有数据。
为 SQL 服务器数据库创建 DSN(数据源名称)。然后通过打开 Microsoft Access 数据库并选择从该 DSN 导入来 select 该 DSN。您应该能够导入准确的 1GB table(架构、数据、所有内容)。
有关使用 DSN 的更多信息:
https://support.office.com/en-us/article/Link-to-SQL-Server-data-0474c16d-a473-4458-9cf7-f369b78d3db8
或者,您可以使用该 DSN link 到 SQL 服务器数据库(而不是导入到 Access table)并完全跳过导入。
INSERT 是否应该是 TRANSACTION 的一部分。在 TRANSACTION 中通常会加快 BULK INSERTS
您可以求助于 SQL 服务器集成服务 (SSIS),而不是编写代码,并在午饭前完成。它以 extension to Visual Studio 的形式提供,以防您的计算机上没有它 SQL 服务器。
使用 SSIS,您可以创建可从命令行或计划任务触发的可重复使用的 SSIS 包。 This guide shows how to pull data from MySQL into SQL Server, but the SQL Server part should be easy to replace with Access.
对注释进行了一些更改,以添加用于命令执行的事务。如果事务不是手动控制的,每次都会自动创建和提交,这是一个耗时的操作
OleDbConnection accCon = new OleDbConnection();
OdbcCommand mySQLCon = new OdbcCommand();
try
{
//connect to mysql
Connect();
mySQLCon.Connection = connection;
//connect to access
accCon.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;" +
@"Data source= " + pathToAccess;
accCon.Open();
var cnt = 0;
while (cnt < 5)
{
if (accCon.State == ConnectionState.Open)
break;
cnt++;
System.Threading.Thread.Sleep(50);
}
if (cnt == 5)
{
ToolBox.logThis("Connection to Access DB did not open. Exit Process");
return;
}
}
catch (Exception e)
{
ToolBox.logThis("Faild to Open connections. msg -> " + e.Message + "\n" + e.StackTrace);
}
//AMK: transaction starts here
var transaction = accCon.BeginTransaction();
OleDbCommand accCmn = new OleDbCommand();
accCmn.Connection = accCon;
accCmn.Transaction = transaction;
//access insert query structure
var insertAccessQuery = "INSERT INTO {0} values({1});";
// key = > tbl name in access, value = > mysql query to b executed
foreach (var table in tblNQuery)
{
try
{
mySQLCon.CommandText = table.Value;
//executed mysql query
using (var dataReader = mySQLCon.ExecuteReader())
{
//variable to hold row data
var rowData = new object[dataReader.FieldCount];
var parameters = "";
//read the result set from mysql query
while (dataReader.Read())
{
//fill rowData with the row values
dataReader.GetValues(rowData);
//build the parameters for insert query
for (var i = 0; i < dataReader.FieldCount; i++)
parameters += "'" + rowData[i] + "',";
parameters = parameters.TrimEnd(',');
//insert to access
accCmn.CommandText = string.Format(insertAccessQuery, table.Key, parameters);
try
{
accCmn.ExecuteNonQuery();
}
catch (Exception exc)
{
ToolBox.logThis("Faild to insert to access db. msg -> " + exc.Message +
"\n\tInsert query -> " + accCmn.CommandText);
}
parameters = "";
}
}
//AMK: transaction commits here if every thing is going well
transaction.Commit();
}
catch (Exception e)
{
ToolBox.logThis("Faild to populate access db. msg -> " + e.Message + "\n" + e.StackTrace);
//AMK: transaction rollback here if there is a problem
transaction.Rollback();
}
}
Disconnect();
accCmn.Dispose();
accCon.Close();
谢谢大家的回答。我刚刚在我的代码中发现了主要问题。大量内存使用(问题 #1)的原因是 ODBC 正在缓存来自 MySQL 的数据,而不管 C# 方法(DataReader)如何。通过选中 DSN 设置中的 Don't cache results of forward-only cursors
复选框可以解决该问题。这也使这个过程稍微快了一点(30%)。然而,更具体的方法仍然是 Brian Pressler 和 Egil Hansen suggested.But 因为他们需要软件安装 and/or 迁移计划,最简单的方法是坚持这段代码。
情况: 我正在创建一个自动任务,它查询 MySQL(通过 ODBC)并将结果集插入 MS Access 数据库 (.mdb),使用OLEDB。
代码:
OleDbConnection accCon = new OleDbConnection();
OdbcCommand mySQLCon = new OdbcCommand();
try
{
//connect to mysql
Connect();
mySQLCon.Connection = connection;
//connect to access
accCon.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;" +
@"Data source= " + pathToAccess;
accCon.Open();
var cnt = 0;
while (cnt < 5)
{
if (accCon.State == ConnectionState.Open)
break;
cnt++;
System.Threading.Thread.Sleep(50);
}
if (cnt == 5)
{
ToolBox.logThis("Connection to Access DB did not open. Exit Process");
return;
}
} catch (Exception e)
{
ToolBox.logThis("Faild to Open connections. msg -> " + e.Message + "\n" + e.StackTrace);
}
OleDbCommand accCmn = new OleDbCommand();
accCmn.Connection = accCon;
//access insert query structure
var insertAccessQuery = "INSERT INTO {0} values({1});";
// key = > tbl name in access, value = > mysql query to b executed
foreach (var table in tblNQuery)
{
try
{
mySQLCon.CommandText = table.Value;
//executed mysql query
using (var dataReader = mySQLCon.ExecuteReader())
{
//variable to hold row data
var rowData = new object[dataReader.FieldCount];
var parameters = "";
//read the result set from mysql query
while (dataReader.Read())
{
//fill rowData with the row values
dataReader.GetValues(rowData);
//build the parameters for insert query
for (var i = 0; i < dataReader.FieldCount; i++)
parameters += "'" + rowData[i] + "',";
parameters = parameters.TrimEnd(',');
//insert to access
accCmn.CommandText = string.Format(insertAccessQuery, table.Key, parameters);
try
{
accCmn.ExecuteNonQuery();
}
catch (Exception exc)
{
ToolBox.logThis("Faild to insert to access db. msg -> " + exc.Message + "\n\tInsert query -> " + accCmn.CommandText );
}
parameters = "";
}
}
}
catch (Exception e)
{
ToolBox.logThis("Faild to populate access db. msg -> " + e.Message + "\n" + e.StackTrace);
}
}
Disconnect();
accCmn.Dispose();
accCon.Close();
问题:
内存使用率非常高 (300MB++) 而 MS Access 文件大小却没有经常变化!似乎插入缓存数据而不是将其保存到磁盘。
速度很慢!我知道我的查询会在几秒钟内执行,但插入过程需要很长时间。
我尝试在 MS Access 中使用准备好的语句并将值作为参数而不是字符串连接插入来创建插入查询。但是我收到此异常消息:
Data type mismatch in criteria expression.
有人知道如何解决这个问题或有更好的方法吗?
您可以创建一个 VBA 宏,它使用 DoCmd.TransferDatabase 方法通过 ODBC 将数据提取到您的 Access 数据库中。它可能也会更快更简单。
要运行 来自外部程序或计划任务的 VBA 代码,只需启动 Access 以使用 /x 命令行开关打开您的文件,它将 运行 导入启动时的宏。 1 GB 的数据仍然需要一段时间。我找到了 article by David Catriel that implemented this approach.
更好的选择是使用不同的数据库引擎后端,例如 SQL Server Express 的免费版本。然后你有更多的选择,它也更强大。如果您需要 MS Access 表格和报告,如果您使用 SQL 服务器,则可以创建一个 ADP 项目文件,或者您可以使用链接表来获取数据。如果满足您的要求,您甚至可以 use Access as a front-end to your MySQL database 而不是复制所有数据。
为 SQL 服务器数据库创建 DSN(数据源名称)。然后通过打开 Microsoft Access 数据库并选择从该 DSN 导入来 select 该 DSN。您应该能够导入准确的 1GB table(架构、数据、所有内容)。
有关使用 DSN 的更多信息: https://support.office.com/en-us/article/Link-to-SQL-Server-data-0474c16d-a473-4458-9cf7-f369b78d3db8
或者,您可以使用该 DSN link 到 SQL 服务器数据库(而不是导入到 Access table)并完全跳过导入。
INSERT 是否应该是 TRANSACTION 的一部分。在 TRANSACTION 中通常会加快 BULK INSERTS
您可以求助于 SQL 服务器集成服务 (SSIS),而不是编写代码,并在午饭前完成。它以 extension to Visual Studio 的形式提供,以防您的计算机上没有它 SQL 服务器。
使用 SSIS,您可以创建可从命令行或计划任务触发的可重复使用的 SSIS 包。 This guide shows how to pull data from MySQL into SQL Server, but the SQL Server part should be easy to replace with Access.
对注释进行了一些更改,以添加用于命令执行的事务。如果事务不是手动控制的,每次都会自动创建和提交,这是一个耗时的操作
OleDbConnection accCon = new OleDbConnection();
OdbcCommand mySQLCon = new OdbcCommand();
try
{
//connect to mysql
Connect();
mySQLCon.Connection = connection;
//connect to access
accCon.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;" +
@"Data source= " + pathToAccess;
accCon.Open();
var cnt = 0;
while (cnt < 5)
{
if (accCon.State == ConnectionState.Open)
break;
cnt++;
System.Threading.Thread.Sleep(50);
}
if (cnt == 5)
{
ToolBox.logThis("Connection to Access DB did not open. Exit Process");
return;
}
}
catch (Exception e)
{
ToolBox.logThis("Faild to Open connections. msg -> " + e.Message + "\n" + e.StackTrace);
}
//AMK: transaction starts here
var transaction = accCon.BeginTransaction();
OleDbCommand accCmn = new OleDbCommand();
accCmn.Connection = accCon;
accCmn.Transaction = transaction;
//access insert query structure
var insertAccessQuery = "INSERT INTO {0} values({1});";
// key = > tbl name in access, value = > mysql query to b executed
foreach (var table in tblNQuery)
{
try
{
mySQLCon.CommandText = table.Value;
//executed mysql query
using (var dataReader = mySQLCon.ExecuteReader())
{
//variable to hold row data
var rowData = new object[dataReader.FieldCount];
var parameters = "";
//read the result set from mysql query
while (dataReader.Read())
{
//fill rowData with the row values
dataReader.GetValues(rowData);
//build the parameters for insert query
for (var i = 0; i < dataReader.FieldCount; i++)
parameters += "'" + rowData[i] + "',";
parameters = parameters.TrimEnd(',');
//insert to access
accCmn.CommandText = string.Format(insertAccessQuery, table.Key, parameters);
try
{
accCmn.ExecuteNonQuery();
}
catch (Exception exc)
{
ToolBox.logThis("Faild to insert to access db. msg -> " + exc.Message +
"\n\tInsert query -> " + accCmn.CommandText);
}
parameters = "";
}
}
//AMK: transaction commits here if every thing is going well
transaction.Commit();
}
catch (Exception e)
{
ToolBox.logThis("Faild to populate access db. msg -> " + e.Message + "\n" + e.StackTrace);
//AMK: transaction rollback here if there is a problem
transaction.Rollback();
}
}
Disconnect();
accCmn.Dispose();
accCon.Close();
谢谢大家的回答。我刚刚在我的代码中发现了主要问题。大量内存使用(问题 #1)的原因是 ODBC 正在缓存来自 MySQL 的数据,而不管 C# 方法(DataReader)如何。通过选中 DSN 设置中的 Don't cache results of forward-only cursors
复选框可以解决该问题。这也使这个过程稍微快了一点(30%)。然而,更具体的方法仍然是 Brian Pressler 和 Egil Hansen suggested.But 因为他们需要软件安装 and/or 迁移计划,最简单的方法是坚持这段代码。