C# 使用OLEDB(VFPOLEDB)对DBF文件运行查询太慢

C# 使用OLEDB(VFPOLEDB)对DBF文件运行查询太慢,c#,visual-foxpro,C#,Visual Foxpro,我正在开发一个界面,用于在WinForm应用程序中显示DBF文件中的数据。 我开始使用OdbcConnection。尽管它工作正常,但由于不支持Visual FoxPro驱动程序子查询的某些限制,我打开了OLEDBVFPOLEDB。现在,我能够运行复杂的查询,但出现了新的困难,必须加以解决。问题是这些查询太慢。比预期慢100倍 下面是演示的代码。 有一个DBF表“PROD”。索引字段PRICE\N用于查询的Where子句中。该表位于运行应用程序的同一台PC上。正如您所看到的,通过ODBCMicr

我正在开发一个界面,用于在WinForm应用程序中显示DBF文件中的数据。 我开始使用OdbcConnection。尽管它工作正常,但由于不支持Visual FoxPro驱动程序子查询的某些限制,我打开了OLEDBVFPOLEDB。现在,我能够运行复杂的查询,但出现了新的困难,必须加以解决。问题是这些查询太慢。比预期慢100倍

下面是演示的代码。 有一个DBF表“PROD”。索引字段PRICE\N用于查询的Where子句中。该表位于运行应用程序的同一台PC上。正如您所看到的,通过ODBCMicrosoft Visual FoxPro驱动程序和OLEDBVFPOLEDB运行查询所花费的时间差别很大

            TimeSpan timeSpanODBC;
        DateTime timeODBC = DateTime.Now;

        OdbcConnection odbcConnection = new OdbcConnection(@"Driver={Microsoft Visual FoxPro Driver};SourceType=DBF;SourceDB=C:\Users\Vakshul\Documents\dbfs;Exclusive=No;Collate=Machine;NULL=NO;DELETED=NO;BACKGROUNDFETCH=NO;");
        odbcConnection.Open();
        OdbcCommand odbcCommand = new OdbcCommand("SELECT utk_ved FROM prod WHERE (price_n='641857')", odbcConnection);
        odbcCommand.ExecuteScalar();
        timeSpanODBC = DateTime.Now - timeODBC;
        double timeOdbcEqual = timeSpanODBC.TotalMilliseconds;
        System.Console.WriteLine("Time spent via ODBC(milliseconds) using '=' to compare - {0}", timeOdbcEqual.ToString());


        timeODBC = DateTime.Now;

        odbcCommand = new OdbcCommand("SELECT utk_ved FROM prod WHERE (price_n like'641857')", odbcConnection);
        odbcCommand.ExecuteScalar();
        timeSpanODBC = DateTime.Now - timeODBC;
        double timeOdbcLike = timeSpanODBC.TotalMilliseconds;
        System.Console.WriteLine("Time spent via ODBC(milliseconds) using 'Like' to compare - {0}", timeOdbcLike.ToString());

        TimeSpan timeSpanOLEDB;
        DateTime timeOLEDB = DateTime.Now;

        OleDbConnection oleDbCon = new OleDbConnection(@"Provider=VFPOLEDB.1;Data Source=C:\Users\Vakshul\Documents\dbfs;Collating Sequence=MACHINE;Mode=Read");
        oleDbCon.Open();
        OleDbCommand oleDbcommand = new OleDbCommand("SELECT utk_ved FROM prod WHERE (price_n = '641857')", oleDbCon);
        oleDbcommand.ExecuteScalar();
        timeSpanOLEDB = DateTime.Now - timeOLEDB;
        double timeOLEDBEqual = timeSpanOLEDB.TotalMilliseconds;
        System.Console.WriteLine("Time spent via OLEDB(milliseconds) using '=' to compare - {0}", timeOLEDBEqual.ToString());

        timeOLEDB = DateTime.Now;

        oleDbcommand = new OleDbCommand("SELECT utk_ved FROM prod WHERE (price_n like '641857')", oleDbCon);
        oleDbcommand.ExecuteScalar();
        timeSpanOLEDB = DateTime.Now - timeOLEDB;
        double timeOLEDLike = timeSpanOLEDB.TotalMilliseconds;
        System.Console.WriteLine("Time spent via OLEDB(milliseconds) using 'Like' to compare - {0}", timeOLEDLike.ToString());

        System.Console.WriteLine("ODBC is faster than OLEDB {0} times using '=' to compare", Math.Round(timeOLEDBEqual / timeOdbcEqual, 0));
        System.Console.WriteLine("ODBC is faster than OLEDB {0} times using 'Like' to compare", Math.Round(timeOLEDBEqual / timeOdbcEqual, 0));
控制台,第一次运行后:

Time spent via ODBC(milliseconds) using '=' to compare - 5,0006
Time spent via ODBC(milliseconds) using 'Like' to compare - 3,5005
Time spent via OLEDB(milliseconds) using '=' to compare - 1630,207
Time spent via OLEDB(milliseconds) using 'Like' to compare - 1755,2228
ODBC is faster than OLEDB 326 times using '=' to compare
ODBC is faster than OLEDB 326 times using 'Like' to compare

Console, after the second run:
Time spent via ODBC(milliseconds) using '=' to compare - 4,5006
Time spent via ODBC(milliseconds) using 'Like' to compare - 4,5005
Time spent via OLEDB(milliseconds) using '=' to compare - 1526,1938
Time spent via OLEDB(milliseconds) using 'Like' to compare - 1595,2026
ODBC is faster than OLEDB 339 times using '=' to compare
ODBC is faster than OLEDB 339 times using 'Like' to compare

Console, after the third run:
Time spent via ODBC(milliseconds) using '=' to compare - 4,0005
Time spent via ODBC(milliseconds) using 'Like' to compare - 3,0004
Time spent via OLEDB(milliseconds) using '=' to compare - 1449,184
Time spent via OLEDB(milliseconds) using 'Like' to compare - 1451,1843
ODBC is faster than OLEDB 362 times using '=' to compare
ODBC is faster than OLEDB 362 times using 'Like' to compare

Console, after the fourth run:
Time spent via ODBC(milliseconds) using '=' to compare - 3,5004
Time spent via ODBC(milliseconds) using 'Like' to compare - 4,5006
Time spent via OLEDB(milliseconds) using '=' to compare - 1475,6874
Time spent via OLEDB(milliseconds) using 'Like' to compare - 1621,2059
ODBC is faster than OLEDB 422 times using '=' to compare
ODBC is faster than OLEDB 422 times using 'Like' to compare
Time spent via ODBC(milliseconds) using '=' to compare - 12,5016
Time spent via ODBC(milliseconds) using 'Like' to compare - 3,5005
Time spent via OLEDB(milliseconds) using '=' to compare - 20,0025
Time spent via OLEDB(milliseconds) using 'Like' to compare - 3,0004
ODBC is faster than OLEDB 2 times using '=' to compare
ODBC is faster than OLEDB 2 times using 'Like' to compare
在此示例中,查询的Where子句中包含一个索引字段PRICE\N。 我还测试了相同的查询,在Where子句中包含非索引字段,而不是索引字段。结果是相同的~1400–1600毫秒。 我的印象是,在OLEDBVFPOLEDB的情况下,不使用索引。 我对结果不满意,我需要使用索引


如果有人有任何建议,我将非常感激。

只是想知道。。。价格列是数字列还是字符串列。如果是数字,那么我想知道VFP OleDb是否正在尝试将您的所有数字转换为用于测试的等效字符串,而不是将引用的字符串转换为价格数据类型的等效数字,正如我预期的自然指数所基于的那样

如果是这样,请尝试测试并将所有WHERE子句分别更改为

其中价格=641857

哪里的价格像641857

但是如果是基于数字的列,那么Like实际上不适用,因为数字是数字,而不像部分字符串匹配

"1" LIKE "1"
"1" LIKE "10"
"1" LIKE "19302"... etc where they all start with a same value
@塞吉伊, 你在做什么有很大的不同。VFP6之后的版本(即2.5或2.6是包含ODBC驱动程序的最后一个包)不存在ODBC驱动程序。IOW ODBC仅支持VFP6引擎。OtohVFPoleDB支持VFP9引擎和所有添加的SQL功能

在这些引擎之间,有一个问题使得文本字段的查询速度变慢: 如果OS代码页与表的代码页不同,并且搜索是在表达式为字符类型的索引上进行的。然后它不使用索引,而是进行表扫描。这个错误在VFP9的初始发布后出现,并没有在AFAIK中纠正

根据=vs like,like隐式地表示ANSI,因此使用==运算符精确匹配,其行为类似于。使用=,如果禁用ANSI,则部分匹配为true

PS:VFPOLEDB,即使在代码页校正之后,也稍微慢一些,但可以忽略不计

下面是我对代码的计时,在一个有1000000行的测试表上:

Time spent via ODBC(milliseconds) using '=' to compare - 41.0023
Time spent via ODBC(milliseconds) using 'Like' to compare - 0
Time spent via OLEDB(milliseconds) using '=' to compare - 68.0038
Time spent via OLEDB(milliseconds) using 'Like' to compare - 2.0002
ODBC is faster than OLEDB 2 times using '=' to compare
ODBC is faster than OLEDB 2 times using 'Like' to compare
以下是第二次运行后的计时:

Time spent via ODBC(milliseconds) using '=' to compare - 1
Time spent via ODBC(milliseconds) using 'Like' to compare - 1.0001
Time spent via OLEDB(milliseconds) using '=' to compare - 3.0001
Time spent via OLEDB(milliseconds) using 'Like' to compare - 0
ODBC is faster than OLEDB 3 times using '=' to compare
ODBC is faster than OLEDB 3 times using 'Like' to compare
Time spent via ODBC(milliseconds) using '=' to compare - 3,0004
Time spent via ODBC(milliseconds) using 'Like' to compare - 2,5003
Time spent via OLEDB(milliseconds) using '=' to compare - 11,0014
Time spent via OLEDB(milliseconds) using 'Like' to compare - 3,5005
ODBC is faster than OLEDB 4 times using '=' to compare
ODBC is faster than OLEDB 4 times using 'Like' to compare
@塞廷·巴索兹

在这些引擎之间,有一个问题使得查询速度变慢 文本字段:如果OS代码页与表的代码页不同,则 正在对表达式为字符的索引执行搜索 类型然后它不使用索引,而是进行表扫描。这个虫子 在VFP9首次发布后浮出水面,未经AFAIK纠正

你抓住了问题的关键

我不知道那个怪癖。我决定看看是否真的是这样。如果您的假设是正确的,那么低速的原因是DBF文件和我的操作系统的代码页不同。为了测试我是否安装了Visual Fox Pro 9I,我以前从未处理过它,并将所有数据传输到一个新表中。然后我在表设计器中打开表,并在PRICE\N字段上创建了一个常规索引。因此,新表和我的操作系统的代码页变得相同

然后我再次运行测试。结果发生了巨大的变化

            TimeSpan timeSpanODBC;
        DateTime timeODBC = DateTime.Now;

        OdbcConnection odbcConnection = new OdbcConnection(@"Driver={Microsoft Visual FoxPro Driver};SourceType=DBF;SourceDB=C:\Users\Vakshul\Documents\dbfs;Exclusive=No;Collate=Machine;NULL=NO;DELETED=NO;BACKGROUNDFETCH=NO;");
        odbcConnection.Open();
        OdbcCommand odbcCommand = new OdbcCommand("SELECT utk_ved FROM prod WHERE (price_n='641857')", odbcConnection);
        odbcCommand.ExecuteScalar();
        timeSpanODBC = DateTime.Now - timeODBC;
        double timeOdbcEqual = timeSpanODBC.TotalMilliseconds;
        System.Console.WriteLine("Time spent via ODBC(milliseconds) using '=' to compare - {0}", timeOdbcEqual.ToString());


        timeODBC = DateTime.Now;

        odbcCommand = new OdbcCommand("SELECT utk_ved FROM prod WHERE (price_n like'641857')", odbcConnection);
        odbcCommand.ExecuteScalar();
        timeSpanODBC = DateTime.Now - timeODBC;
        double timeOdbcLike = timeSpanODBC.TotalMilliseconds;
        System.Console.WriteLine("Time spent via ODBC(milliseconds) using 'Like' to compare - {0}", timeOdbcLike.ToString());

        TimeSpan timeSpanOLEDB;
        DateTime timeOLEDB = DateTime.Now;

        OleDbConnection oleDbCon = new OleDbConnection(@"Provider=VFPOLEDB.1;Data Source=C:\Users\Vakshul\Documents\dbfs;Collating Sequence=MACHINE;Mode=Read");
        oleDbCon.Open();
        OleDbCommand oleDbcommand = new OleDbCommand("SELECT utk_ved FROM prod WHERE (price_n = '641857')", oleDbCon);
        oleDbcommand.ExecuteScalar();
        timeSpanOLEDB = DateTime.Now - timeOLEDB;
        double timeOLEDBEqual = timeSpanOLEDB.TotalMilliseconds;
        System.Console.WriteLine("Time spent via OLEDB(milliseconds) using '=' to compare - {0}", timeOLEDBEqual.ToString());

        timeOLEDB = DateTime.Now;

        oleDbcommand = new OleDbCommand("SELECT utk_ved FROM prod WHERE (price_n like '641857')", oleDbCon);
        oleDbcommand.ExecuteScalar();
        timeSpanOLEDB = DateTime.Now - timeOLEDB;
        double timeOLEDLike = timeSpanOLEDB.TotalMilliseconds;
        System.Console.WriteLine("Time spent via OLEDB(milliseconds) using 'Like' to compare - {0}", timeOLEDLike.ToString());

        System.Console.WriteLine("ODBC is faster than OLEDB {0} times using '=' to compare", Math.Round(timeOLEDBEqual / timeOdbcEqual, 0));
        System.Console.WriteLine("ODBC is faster than OLEDB {0} times using 'Like' to compare", Math.Round(timeOLEDBEqual / timeOdbcEqual, 0));
第一次运行后:

Time spent via ODBC(milliseconds) using '=' to compare - 5,0006
Time spent via ODBC(milliseconds) using 'Like' to compare - 3,5005
Time spent via OLEDB(milliseconds) using '=' to compare - 1630,207
Time spent via OLEDB(milliseconds) using 'Like' to compare - 1755,2228
ODBC is faster than OLEDB 326 times using '=' to compare
ODBC is faster than OLEDB 326 times using 'Like' to compare

Console, after the second run:
Time spent via ODBC(milliseconds) using '=' to compare - 4,5006
Time spent via ODBC(milliseconds) using 'Like' to compare - 4,5005
Time spent via OLEDB(milliseconds) using '=' to compare - 1526,1938
Time spent via OLEDB(milliseconds) using 'Like' to compare - 1595,2026
ODBC is faster than OLEDB 339 times using '=' to compare
ODBC is faster than OLEDB 339 times using 'Like' to compare

Console, after the third run:
Time spent via ODBC(milliseconds) using '=' to compare - 4,0005
Time spent via ODBC(milliseconds) using 'Like' to compare - 3,0004
Time spent via OLEDB(milliseconds) using '=' to compare - 1449,184
Time spent via OLEDB(milliseconds) using 'Like' to compare - 1451,1843
ODBC is faster than OLEDB 362 times using '=' to compare
ODBC is faster than OLEDB 362 times using 'Like' to compare

Console, after the fourth run:
Time spent via ODBC(milliseconds) using '=' to compare - 3,5004
Time spent via ODBC(milliseconds) using 'Like' to compare - 4,5006
Time spent via OLEDB(milliseconds) using '=' to compare - 1475,6874
Time spent via OLEDB(milliseconds) using 'Like' to compare - 1621,2059
ODBC is faster than OLEDB 422 times using '=' to compare
ODBC is faster than OLEDB 422 times using 'Like' to compare
Time spent via ODBC(milliseconds) using '=' to compare - 12,5016
Time spent via ODBC(milliseconds) using 'Like' to compare - 3,5005
Time spent via OLEDB(milliseconds) using '=' to compare - 20,0025
Time spent via OLEDB(milliseconds) using 'Like' to compare - 3,0004
ODBC is faster than OLEDB 2 times using '=' to compare
ODBC is faster than OLEDB 2 times using 'Like' to compare
第二次运行后:

Time spent via ODBC(milliseconds) using '=' to compare - 1
Time spent via ODBC(milliseconds) using 'Like' to compare - 1.0001
Time spent via OLEDB(milliseconds) using '=' to compare - 3.0001
Time spent via OLEDB(milliseconds) using 'Like' to compare - 0
ODBC is faster than OLEDB 3 times using '=' to compare
ODBC is faster than OLEDB 3 times using 'Like' to compare
Time spent via ODBC(milliseconds) using '=' to compare - 3,0004
Time spent via ODBC(milliseconds) using 'Like' to compare - 2,5003
Time spent via OLEDB(milliseconds) using '=' to compare - 11,0014
Time spent via OLEDB(milliseconds) using 'Like' to compare - 3,5005
ODBC is faster than OLEDB 4 times using '=' to compare
ODBC is faster than OLEDB 4 times using 'Like' to compare
谢谢,塞廷·巴索兹。评论非常棒:

即使不允许我更改生产DBF文件的代码页,但至少现在当我知道发生了什么时,我可以轻松地摆脱负担。

@Sergiy, 我可以为您提供一个解决方案:

string sqlEq = "SELECT utk_ved FROM prod WHERE Price_N = '641857'";
string sqlLike = "SELECT utk_ved FROM prod WHERE Price_N like '641857'";

TimeSpan timeSpanODBC;
DateTime timeODBC = DateTime.Now;

OdbcConnection odbcConnection = new OdbcConnection(@"Driver={Microsoft Visual FoxPro Driver};SourceType=DBF;SourceDB=C:\Users\Vakshul\Documents\dbfs;Exclusive=No;Collate=Machine;NULL=NO;DELETED=NO;BACKGROUNDFETCH=NO;");
odbcConnection.Open();
OdbcCommand odbcCommand = new OdbcCommand(sqlEq, odbcConnection);
odbcCommand.ExecuteScalar();
timeSpanODBC = DateTime.Now - timeODBC;
double timeOdbcEqual = timeSpanODBC.TotalMilliseconds;
System.Console.WriteLine("Time spent via ODBC(milliseconds) using '=' to compare - {0}", timeOdbcEqual.ToString());


timeODBC = DateTime.Now;

odbcCommand = new OdbcCommand(sqlLike, odbcConnection);
odbcCommand.ExecuteScalar();
timeSpanODBC = DateTime.Now - timeODBC;
double timeOdbcLike = timeSpanODBC.TotalMilliseconds;
System.Console.WriteLine("Time spent via ODBC(milliseconds) using 'Like' to compare - {0}", timeOdbcLike.ToString());

TimeSpan timeSpanOLEDB;
DateTime timeOLEDB = DateTime.Now;

OleDbConnection oleDbCon = new OleDbConnection(@"Provider=VFPOLEDB.1;Data Source=C:\Users\Vakshul\Documents\dbfs;Collating Sequence=MACHINE;Mode=Read");
oleDbCon.Open();
new OleDbCommand("set enginebehavior 80", oleDbCon).ExecuteNonQuery();
OleDbCommand oleDbcommand = new OleDbCommand(sqlEq, oleDbCon);
oleDbcommand.ExecuteScalar();
timeSpanOLEDB = DateTime.Now - timeOLEDB;
double timeOLEDBEqual = timeSpanOLEDB.TotalMilliseconds;
System.Console.WriteLine("Time spent via OLEDB(milliseconds) using '=' to compare - {0}", timeOLEDBEqual.ToString());

timeOLEDB = DateTime.Now;

oleDbcommand = new OleDbCommand(sqlLike, oleDbCon);

oleDbcommand.ExecuteScalar();
timeSpanOLEDB = DateTime.Now - timeOLEDB;
double timeOLEDLike = timeSpanOLEDB.TotalMilliseconds;
System.Console.WriteLine("Time spent via OLEDB(milliseconds) using 'Like' to compare - {0}", timeOLEDLike.ToString());
注意同一连接上的这一行:

这不会影响你的结果,但会得到你想要的。在我脑海中,它可能影响的唯一地方是分组查询。认为您不会以旧的buggy VFP方式编写分组查询,您应该是安全的

我在没有设置EngineBhavior 80的情况下的计时:

Time spent via ODBC(milliseconds) using '=' to compare - 4.0002
Time spent via ODBC(milliseconds) using 'Like' to compare - 1.0001
Time spent via OLEDB(milliseconds) using '=' to compare - 352.0201
Time spent via OLEDB(milliseconds) using 'Like' to compare - 659.0377
Time spent via ODBC(milliseconds) using '=' to compare - 3.0001
Time spent via ODBC(milliseconds) using 'Like' to compare - 2.0002
Time spent via OLEDB(milliseconds) using '=' to compare - 15.0008
Time spent via OLEDB(milliseconds) using 'Like' to compare - 3.0002
以及设置EngineBhavior 80:

Time spent via ODBC(milliseconds) using '=' to compare - 4.0002
Time spent via ODBC(milliseconds) using 'Like' to compare - 1.0001
Time spent via OLEDB(milliseconds) using '=' to compare - 352.0201
Time spent via OLEDB(milliseconds) using 'Like' to compare - 659.0377
Time spent via ODBC(milliseconds) using '=' to compare - 3.0001
Time spent via ODBC(milliseconds) using 'Like' to compare - 2.0002
Time spent via OLEDB(milliseconds) using '=' to compare - 15.0008
Time spent via OLEDB(milliseconds) using 'Like' to compare - 3.0002

你看到了吗?谢谢,斯图亚特。我以前没看过,所以我读得很透彻。谢谢,@stuartd。我以前没看过,所以我读得很透彻。我尝试使用TABLEVALIDATE=0,但没有效果。一切都没有改变。速度太慢,很难生活。我猜是a
你不用。但为什么呢?我甚至部署了一个虚拟机,在那里安装了所有需要的软件,但结果是一样的:那么在列上定义了CDX索引?你看到了吗?另外,你说你正在使用VFPOLEDB驱动程序,因为本地驱动程序不支持子查询:我记得这是我在VFP开发时的一个痛点,那是在当年的数字中有很多“9”的时候,尽管这是一个痛点,但可以通过创造性地使用外部联接来解决。你真的需要子查询吗?我用VisualFoxPro重新标记了你的问题,希望你能从仍然使用iTiDrapp的人那里得到帮助,“price\n”是一个字符串列。这就是Where子句中使用引号的原因:Where price_n='641857'。我在这个测试中使用LIKE的原因仅仅是希望使用它可以增加对正在发生的事情的了解。我应该补充一下,我在Windows7、VisualStudio2012、.NETFramework 4上进行了测试。5@SergiyVakshul,你们有价格指数吗?这是可能的多键索引表达式中的第一列?我过去使用过VFP OleDb,查询速度非常快。。即使是在.NET3.5和.NET4下。但对于生产,我强烈建议将查询参数化。我有一个关于价格的索引。表中有几个索引,其中一个是关于价格的。这个索引不是多个。它只是一列的索引。至于参数化查询,我也检查了它——结果是一样的——太慢了。@SergiyVakshul,最后一个想法。。。数据库是静态的吗?即:不更改内容和固定数据,这些数据不会被基于web的内容读取/写入,但可能通过其他方式填充。。。如果是这样,并且您将表/cdx文件作为基于web的/C查询的只读权限,则O/S不需要对任何可能的锁定执行任何开销,即使它只是以任何方式进行查询。还有,表格有多大?比如10是假的,不是吗?LIKE是隐式启用的,其行为就像您使用了==。同样,您非常有帮助。事实上,使用与VisualFoxpro8.0兼容的数据引擎可以避免查询速度的降低。它还允许从select运行查询,如select t.*。。。作为t。希望我能够运行更复杂的查询。你的帮助真是无价之宝。