我正在尝试编写一个VB.Net程序,每天为MSAccess表保存1-2百万个5字段记录(加上索引ID)。保存过程目前需要13-20个小时,这显然是不对的。
它是一个平面表,索引最小,目前只有156MB。除了一个双字段外,字段是小字符串,日期或长整数。磁盘本身是一个15,000 SATA,仅用于此文件。在保存例程期间,计算机和程序没有执行任何其他操作。保存例程是一个简单的FOR-NEXT循环,它为数据集中的每条记录发出一个简短的INSERT语句。
任何人都有关于我需要改变什么以使其更好地工作的想法?
答案 0 :(得分:2)
可以在任何DBMS上工作以大幅加速插入的技巧是在批量插入数据之前暂时禁用索引,外键和约束 - 然后在数据库中的数据之后再次启用它们。
特别是索引可以成为顺序插入的性能杀手,它至少要先填充一个表(有时是2!),然后在已经填充的数据上创建索引,而不是在索引中插入索引。地点。在这种情况下,您可能需要删除索引,然后重新创建它。
然后,正如大多数其他海报已经说过的那样,如果你可以一串一行地插入一行,那真的是浪费时间。如果您打开表没有锁定或只有乐观锁定,您将获得较小的速度提升。
然后你可以通过使用DAO记录集而不是ADO来获得另一个微小的增量 - 我在VB6开发的时候注意到了这一点,可能这不再是ADO.NET的情况
答案 1 :(得分:1)
您是否启用了“自动提交”功能?
这会让你慢下来,因为每个插页都需要用语法书写 在处理下一个之前到磁盘。
尝试手动提交约1000个插入内容。
答案 2 :(得分:1)
好的,从长时间的午餐回来。
PAUL,PINEDA,您的建议是将PK编入索引是正确的。摆脱了索引,突然它每分钟存储40,000条记录,足以在一小时内完成一整天的价值。它根本不影响使用数据的应用程序的速度。
其他人慷慨的人们......我会在一天剩下的时间里撇开你的建议,希望能让它变得更好。
你非常乐于助人。我欠你一杯啤酒。
答案 3 :(得分:1)
您应该真正管理批量插入。每个插入都有一堆开销,并且在For next循环中一次执行一行,就会浪费超过计算机功率的2/3。如果数据一次只有一行,则需要在将批量数据插入数据库之前构建一个缓冲区来收集它。 Kibbee建议将数据写入csv文件然后将其转储到数据库,如果你需要写下数据,这是一个很好的方法。我建议一次在内存中收集数据几分钟。
答案 4 :(得分:0)
ARVO;关闭病毒扫描程序并进入锁定模式= 0帮助...现在每分钟最多1300条记录(是的,我的意思是分钟),但这仍然很慢。
STEPHBU:我的C技能很小,但据我所知,你做的事情和我的情况差不多。你的“JetConnection”字符串比我的简单明了......这是我的
Me.connSPY.ConnectionString = "Jet OLEDB:Global Partial Bulk Ops=2;" & _
"Jet OLEDB:Registry Path=;Jet OLEDB:" & _
"Database Locking Mode=0;" & _
"Data Source=""E:\SPIRE.mdb"";" & _
"Mode=Share Deny None;" & _
"Jet OLEDB:Engine Type=5;" & _
"Provider=""Microsoft.Jet.OLEDB.4.0"";" & _
"Jet OLEDB:System database=;" & _
"Jet OLEDB:SFP=False" & _
";persist security info=False;" & _
"Extended Properties=;" & _
"Jet OLEDB:Compact Without Replica Repair=False;" & _
"Jet OLEDB:Encrypt Database=False;" & _
"Jet OLEDB:Create System Database=False;" & _
"Jet OLEDB:Don't Copy Locale on Compact=False;" & _
"User ID=Admin;" & _
"Jet OLEDB:Global Bulk Transactions=1"
答案 5 :(得分:0)
我建议使用索引顺序访问方法 - ISAM。它允许您直接将数据从连接的记录源传输到第二个动态连接的数据源。
使用OleDbConnection或类似对象打开连接
使用ISAM格式代替 FROM
运行查询连接Sytax是这样的:
private void PopulateMDB(string ExportPath, int iID)
{
string cnnStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + ExportPath;
OleDbConnection oConn = new OleDbConnection(cnnStr);
string q = @"INSERT
INTO PensionData ([ID]
,[Recipient Name]
,[Gross Amt]
,[Retirement Date]
,[Plan])
select id as [ID]
,name as [Recipient Name]
,gross_amt as [Gross Amt]
,eff_dt as [Retirement Date]
,pln as [Plan]
FROM [ODBC;Driver=SQL Server;SERVER=euddbms.d;DATABASE=DBName;UID=;PWD=;].tableName
WHERE id = " + iID;
oConn.Open();
try
{
OleDbCommand oCmd = new OleDbCommand(q, oConn);
oCmd.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
finally
{
oConn.Close();
oConn = null;
}
}
ISAM内容::
CSV [文本;数据库= C:_PATH \; HDR =是]。[FILE.CSV]
访问 [MS Access; Database = C:\ Path \ File.mdb]。[AccessTableName]
Excel中 [Excel 8.0; HDR =是; IMEX = 1; MaxScanRows = 16;数据库= C:\ Path \ File.xls]。[表$]
SQL Server [ODBC; Driver = SQL Server; SERVER =; DATABASE =; UID =; PWD =;]。[表格]
http://support.microsoft.com/kb/321686 http://support.microsoft.com/kb/200427
答案 6 :(得分:0)
CodeSlave .... 这是一个有效的观点,如果一切顺利,那么我将不得不花钱购买SQL Server或类似的东西,更不用说几台电脑了。目前,我不想投入现金或学习曲线。
安德森先生...... 还没有尝试过,我会的。但就目前而言,其他建议将我的10-20小时节省时间缩短到15分钟,所以我是一个非常开心的露营者。
答案 7 :(得分:0)
每天1-2万个5字段记录(加上索引ID)到MSAccess表。
冒着说明什么应该是显而易见的风险。你正在解决错误的问题。
转储MS-Access并将其推送到MS-SQL服务器上。如果你以后真的必须通过MS-Access访问数据,那么只需创建一个到MS-SQL服务器表的表链接。
即使每条记录有5个字节,你也会在不到一年的时间内超过MS Access 2003的2GB表/数据库大小限制(不幸的是,MS Access 2007也是如此)。
答案 8 :(得分:0)
Doofledorfer:这里有5行示例输入,正如您所问,虽然我确实认为我现在正在使用块插入建议并且主键未编入索引
INSERT INTO Ticks (Symbol, TickDate, TickTime, TickPRice, TickVolume)
VALUES ('SPY', #11/28/2008#, #09:30:00#, 88.63, 200);
INSERT INTO Ticks (Symbol, TickDate, TickTime, TickPRice, TickVolume)
VALUES ('SPY', #11/28/2008#, #09:30:00#, 88.62, 400);
INSERT INTO Ticks (Symbol, TickDate, TickTime, TickPRice, TickVolume)
VALUES ('SPY', #11/28/2008#, #09:30:00#, 88.62, 100);
INSERT INTO Ticks (Symbol, TickDate, TickTime, TickPRice, TickVolume)
VALUES ('SPY', #11/28/2008#, #09:30:00#, 88.62, 300);
INSERT INTO Ticks (Symbol, TickDate, TickTime, TickPRice, TickVolume)
VALUES ('SPY', #11/28/2008#, #09:30:00#, 88.62, 127);
答案 9 :(得分:0)
ops,错过了你的一个问题STEPHBU ...... 我允许索引ID自动递增,而不是尝试在INSERT语句中分配它。但是好好想一想!
答案 10 :(得分:0)
(免责声明:我对Access一无所知)
任何分析工具说什么? (任务管理器会给你一些线索 - 在显示屏上添加更多列以查看I / O,VM使用情况等)?它是在进行大量的磁盘访问,还是所有的CPU?是否消耗了大量内存?
提及索引是因为我可能需要在每次INSERT之后更新 - 你可以关闭索引,进行创建,然后将整个文件编入索引吗?
时间线性吗? (即如果您创建10%大小的文件,则需要10%的时间)?
答案 11 :(得分:0)
假设你不能批量插入,只是做了一个示例应用程序(C#,抱歉 - 但VB.NET将类似)来创建目录,表并执行一些插入。我暂时跳过了对ID的PK约束。
这给了我大约1000行,在550毫秒内运行在带有省电的笔记本电脑驱动器上的病毒扫描程序的VM中。你应该能够通过快速驱动器轻松击败它。差异在哪里?
一个想法是你如何插入ID? INSERT语句自动生成并省略?或者您插入了一个值,该列标记为PK?后者肯定会触发索引搜索(你的大量读取IO ???)来验证数据对表的唯一性?
using System;
using System.Data;
using System.Data.OleDb;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
String jetConnection = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=C:\\jetsample.mdb;";
ADOX.CatalogClass cat = new ADOX.CatalogClass();
cat.Create(jetConnection);
using(OleDbConnection conn = new OleDbConnection(jetConnection))
{
conn.Open();
using(OleDbCommand cmd = new OleDbCommand("CREATE TABLE test ([ID] INTEGER, [TestDouble] DOUBLE, [TestText] TEXT, [TestDate] DATE, [TestInt] INTEGER)",conn))
{
cmd.CommandType = CommandType.Text;
cmd.ExecuteNonQuery();
}
using (OleDbCommand cmd = new OleDbCommand("INSERT INTO [Test] VALUES (@id, @testDouble, @testText, @testDate, @testInt)", conn))
{
OleDbParameter id = cmd.Parameters.Add("@id", OleDbType.Integer);
OleDbParameter testDouble = cmd.Parameters.Add("@testDouble", OleDbType.Double);
OleDbParameter testText = cmd.Parameters.Add("@testText", OleDbType.VarWChar);
OleDbParameter testDate = cmd.Parameters.Add("@testDate", OleDbType.Date);
OleDbParameter testInt = cmd.Parameters.Add("@testInt", OleDbType.Integer);
DateTime start = DateTime.Now;
for (int index = 1; index <= 2000000; index++)
{
if (index % 1000 == 0)
{
System.Diagnostics.Debug.WriteLine(((TimeSpan)(DateTime.Now - start)).Milliseconds);
start = DateTime.Now;
}
id.Value = index;
testDouble.Value = index;
testText.Value = String.Format("{0} DBL", index);
testDate.Value = DateTime.Now.AddMilliseconds(index);
testInt.Value = index;
cmd.ExecuteNonQuery();
}
}
}
}
}
}
答案 12 :(得分:0)
尝试锁定模式= 0 - 这是页面级别。 800 rps(每秒记录数)每10分钟给出480000条记录 - 你的意思是800转?
答案 13 :(得分:0)
以下是关于分隔文本文件VBScript的附加查询的一些注意事项,我担心,但它可能会有所帮助。
Set cn = CreateObject("ADODB.Connection")
strFile="C:\ltd.mdb"
strCon="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" _
& strFile & ";"
cn.Open strCon
strSQL="INSERT INTO tableX ( Name1,Name2 ) " _
& "SELECT Name1,Name2 " _
& "FROM [ltd.txt] IN '' [Text;Database=c:\docs\;HDR=YES;]"
cn.Execute strSQL
答案 14 :(得分:0)
只有一个连接打开,它是一个单用户系统。
试图关闭我的防病毒软件,但它每秒只能保存800条记录。
我不熟悉“锁定”,但我的OLEDB连接字符串包含“锁定模式= 1”
顺便说一下,如果它相关,那么VM大小在几分钟后就会增长到157,000K。
答案 15 :(得分:0)
您已向MSAccess数据库(mdb)打开了多少个连接?从我所看到的,单用户模式比多用户模式快得多。任何打开的MS Access程序都算作单个连接(您可以使用ldbview工具查看ldb文件内部。)
您是使用行级锁定还是页面级锁定?从某些Access版本(2000?)开始,它默认为行级锁定;我认为页面级锁定会更快。
你有没有运行防病毒软件?他们可以拦截文件操作并显着减慢整个过程。
答案 16 :(得分:0)
您可以尝试将数据写入CSV文件,然后使用单个命令轻松将数据导入访问。这可能会显着加快这一过程。
答案 17 :(得分:0)
谢谢,Doofledorfer。
数据来自实时数据供应商的互联网,采用专有的“矢量”格式,我将其解析为数据集。我在开始保存例程之前解析整个文件。是的,如果我可以将数据集“直接”导入数据库,而不是单独保存每条记录,那就太好了。但我不知道如何做到这一点。
答案 18 :(得分:0)
首先,尝试使用Access中的众多导入选项之一。 (数据来自哪里?是分隔的还是长度的?你如何用VB解析它?)
您应该能够创建没有索引的表。通过拒绝Access的提议添加一个。但是先完成导入工作。
答案 19 :(得分:0)
感谢您的提问,保罗。
页面文件使用率为600MB,CPU大部分时间都在5%左右,每20秒左右就会出现80%的峰值。内存:总共2G,可用1.3G,系统缓存为1G。
是的,它似乎是线性的,前15,000条记录需要10分钟。
索引的AS,我没有尝试过,但如果你不至少索引ID字段,Access总会抱怨。
I / O读取似乎很多,但是,运行20分钟后只有6M,只有25,000条记录。