【问题标题】:Running queries on DBF files using OLEDB(VFPOLEDB) is too slow使用 OLEDB(VFPOLEDB) 对 DBF 文件运行查询太慢
【发布时间】:2015-10-17 21:29:36
【问题描述】:

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

下面是演示代码。 有一个 DBF 表“PROD”。索引字段 PRICE_N 用于查询的 Where 子句。该表位于运行应用程序的同一台 PC 上。如您所见,通过 ODBC(Microsoft Visual FoxPro 驱动程序)和 OLEDB(VFPOLEDB)运行查询所花费的时间差别很大。

            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

在此示例中,索引字段 PRICE_N 包含在查询的 Where 子句中。 我还测试了相同的查询,包括 Where 子句中的非索引字段而不是索引字段。结果是相同的 ~ 1400 – 1600 毫秒。 我的印象是,在 OLEDB(VFPOLEDB) 的情况下不使用索引。 我对结果不满意,我需要使用索引。

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

【问题讨论】:

  • 你见过this article吗?
  • 谢谢,@stuartd。我以前没看过,所以我仔细阅读了。
  • 谢谢,@stuartd。我以前没看过,所以我仔细阅读了它。我尝试使用 TABLEVALIDATE=0 但无济于事。什么也没有变。速度如此之低,以至于很难相信。我猜没有使用索引。但为什么会这样?我什至部署了一个虚拟机,在那里安装了所有需要的软件,但结果相同:(
  • 那么列上定义了 CDX 索引吗?你见过this question吗?
  • 另外,您说您正在使用 VFPOLEDB 驱动程序,因为本机驱动程序不支持子查询:我记得当我是 VFP 开发人员时,这是一个痛苦的背影(当年份编号有里面有很多“九”)虽然这很痛苦,但可以通过创造性地使用外连接来解决。你真的需要子查询吗? (我已经用Visual-FoxPro 重新标记了你的问题,希望你能从仍在使用它的人那里得到帮助)

标签: c# visual-foxpro


【解决方案1】:

@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());

注意这一行在同一连接上

new OleDbCommand("set enginebehavior 80", oleDbCon).ExecuteNonQuery();

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

没有“Set EngineBehavior 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

并使用“Set EngineBehavior 80”:

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

【讨论】:

  • 再次,您非常有帮助。事实上,使用与 Visual FoxPro 8.0 兼容的数据引擎可以避免查询速度的下降。它还允许运行诸如“select t.* from (select ...) as t”之类的查询。希望我能够运行更复杂的查询。您的帮助非常宝贵。
【解决方案2】:

只是想知道...您的“Price_n”列是 NUMERIC 还是 STRING 列。如果是数字,那么我想知道 VFP OleDb 是否正在尝试将所有数字转换为 STRING 等效项以进行测试,而不是将引用的字符串转换为 Price_n 数据类型的数字等效项,因为我希望自然索引基于.

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

哪里 price_n = 641857

WHERE price_n 喜欢 641857

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

"1" LIKE "1"
"1" LIKE "10"
"1" LIKE "19302"... etc where they all start with a same value

【讨论】:

  • 嗨 DRapp,“price_n”是一个 STRING 列。这就是为什么在 Where 子句中使用引号:WHERE (price_n='641857')。我在这个测试中使用 LIKE 的原因只是希望使用它可以使正在发生的事情更加清晰。我应该添加我在 Windows 7、Visual Studio 2012、.Net Framework 4.5 上进行了测试
  • @SergiyVakshul,你有 PRICE_N 的索引吗?那是可能的多键索引表达式中的第一列?我过去使用过 VFP OleDb,并且查询速度非常快.. 即使在 .Net 3.5 和 4 下。不过,对于生产,我强烈建议对查询进行参数化。
  • 我在 PRICE_N 上有一个索引。表上有几个索引,其中一个在 PRICE_N 上。这个索引不是多重的。它只是一列的索引。至于参数化查询,我也检查过——结果是一样的——太慢了。
  • @SergiyVakshul,最后一个想法......是数据库静态(即:不更改内容和不会被基于 Web 的内容读取/写入的固定数据,但可能由其他方式...如果是这样,并且您将表/cdx 文件设置为基于 Web/C# 查询的只读权限,则 O/S 不需要对任何可能的锁定进行任何开销,即使它只是无论如何查询。(还有,表有多大)
  • "1" LIKE "10" 是假的,不是吗? LIKE 是隐式 ANSI ON 并且表现得好像你使用了 ==。
【解决方案3】:

@Sergiy, 你在做什么有很大的不同。 VFP6 之后的版本不存在 ODBC 驱动程序(即 2.5 或 2.6 是最后一个包含 ODBC 驱动程序的软件包)。 IOW ODBC 仅支持 VFP6 引擎。 OTOH VFPOLEDB 支持 VFP9 引擎(以及所有那些带有花里胡哨的附加 SQL 功能)。

在这些引擎之间,存在一个问题,使文本字段的查询速度变慢: 如果操作系统代码页与表的代码页不同,并且搜索是在其表达式为字符类型的索引上完成的。然后它不使用索引而是进行表扫描。这个“错误”在 VFP9 的初始版本之后浮出水面,并且没有更正 AFAIK。

根据 = vs like,like 是隐式的 ANSI,因此使用 == 运算符(完全匹配)的行为类似于 。使用 =,如果 ANSI 为 OFF,则部分匹配为真。

PS:VFPOLEDB,即使在更正了代码页之后,速度也会稍慢但可以忽略不计。

这是我对你的代码的计时,在一个有 1,000,000 行的测试表上:

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

【讨论】:

    【解决方案4】:

    @Cetin Basoz,

    在这些引擎之间,存在导致查询速度变慢的问题 文本字段:如果操作系统代码页与表的代码页不同,并且 正在对表达式为字符的索引进行搜索 类型。然后它不使用索引而是进行表扫描。这个“漏洞” 在 VFP9 最初发布后浮出水面,并且未更正 AFAIK。

    你抓住了问题的关键!

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

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

    第一次运行后:

    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 - 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
    

    谢谢,Cetin Basoz。评论太棒了:)

    即使我不被允许更改生产 DBF 文件的代码页,但至少现在当我知道发生了什么时,我可以松一口气。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-24
      • 1970-01-01
      • 2015-03-13
      • 2023-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多