Запросы к файлам DBF с использованием OLEDB (VFPOLEDB) выполняются слишком медленно

Я работаю над интерфейсом для отображения данных из файлов DBF в приложении WinForm. Я начал использовать OdbcConnection. Несмотря на то, что это сработало, из-за определенного ограничения драйвера Visual FoxPro (подзапросы не поддерживаются) я включил использование OLEDB (VFPOLEDB). Теперь я могу выполнять сложные запросы, но возникли новые трудности, которые необходимо решить. Проблема в том, что эти запросы слишком медленные. В 100 раз медленнее, чем ожидалось.

Ниже приведен код для демонстрации. Имеется таблица DBF «PROD». Индексированное поле PRICE_N используется в предложении Where запроса. Таблица находится на том же ПК, на котором запущено приложение. Как видите, время, затрачиваемое на выполнение запроса через ODBC (Microsoft Visual FoxPro Driver) и 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) индексы не используются. Результат меня не устраивает, и мне нужно использовать индексы.

Если у кого-то есть предложения, буду очень признателен.


person Sergiy Vakshul    schedule 17.10.2015    source источник
comment
Вы видели эту статью?   -  person stuartd    schedule 18.10.2015
comment
Спасибо, @stuartd. Я его раньше не видел, поэтому внимательно прочитал.   -  person Sergiy Vakshul    schedule 18.10.2015
comment
Спасибо, @stuartd. Я его раньше не видел, поэтому внимательно прочитал. Я пытался использовать TABLEVALIDATE = 0, но безуспешно. Ничего не изменилось. Скорость настолько низкая, что жить трудно. Полагаю, индексы не используются. Но почему так? Я даже развернул виртуальную машину, установил туда все необходимое ПО, но результат тот же :(   -  person Sergiy Vakshul    schedule 18.10.2015
comment
Значит, в столбцах определены индексы CDX? Вы видели этот вопрос?   -  person stuartd    schedule 19.10.2015
comment
Кроме того, вы говорите, что используете драйвер VFPOLEDB, потому что собственный драйвер не поддерживает подзапросы: я помню, что это было неприятно, когда я был разработчиком VFP (это было еще тогда, когда в номере года было много девяток. ), и хотя это было проблемой, ее можно было обойти с помощью творческого использования внешних соединений. Вам действительно нужны подзапросы? (Я пометил ваш вопрос пометкой Visual-FoxPro в надежде, что вы сможете получить помощь от того, кто до сих пор его использует)   -  person stuartd    schedule 19.10.2015
comment
Да, я видел упомянутое вами обсуждение. Они договорились, что автоматически будут использоваться индексы CDX (в моем случае это файлы CDX и FPT). Но я не думаю, что в моем случае используются индексы. Вы спрашиваете, действительно ли мне нужны подзапросы. К сожалению, конструкция «select t. * From (select…) as t» не работает. Без этого мне будет слишком сложно получить то, что я хочу. В настоящее время я помещаю результаты своих подзапросов в таблицы #temp SQL-сервера и работаю с ними там, но я хотел бы вернуться к VFPOLEDB и поработать с ними. Спасибо за уделенное время, Стюарт.   -  person Sergiy Vakshul    schedule 19.10.2015


Ответы (4)


@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
person Cetin Basoz    schedule 21.10.2015
comment
снова вы очень помогли. Действительно, использование совместимости движка данных с Visual FoxPro 8.0 позволяет избежать снижения скорости запроса. Он также позволяет выполнять такие запросы, как select t. * From (select ...) as t. Надеюсь, я смогу выполнять более сложные запросы. Ваша помощь действительно неоценима. - person Sergiy Vakshul; 22.10.2015

Просто интересно ... ваш столбец "Price_n" - это NUMERIC или STRING столбец. Если числовой, то мне интересно, пытается ли VFP OleDb преобразовать все ваши числовые значения в эквивалент STRING для тестирования вместо преобразования цитируемой строки в числовой эквивалент типа данных Price_n, поскольку я ожидал бы, что естественный индекс будет основан на .

Если да, попробуйте протестировать и изменить все предложения WHERE соответственно на

ГДЕ price_n = 641857

WHERE price_n Нравится 641857

но если столбец на основе чисел, то лайк действительно не будет применяться, потому что число является числом, а не как частичное совпадение строки, например

"1" LIKE "1"
"1" LIKE "10"
"1" LIKE "19302"... etc where they all start with a same value
person DRapp    schedule 19.10.2015
comment
Привет, DRapp, "цена_n" - это столбец STRING. Вот почему кавычки используются в предложении Where: WHERE (price_n = '641857'). Причина, по которой я использовал LIKE в этом тесте, была просто надежда, что его использование может внести некоторую ясность в происходящее. Мне следовало добавить, что я проводил свои тесты в Windows 7, Visual Studio 2012, .Net Framework 4.5 - person Sergiy Vakshul; 20.10.2015
comment
@SergiyVakshul, у вас есть индекс PRICE_N? и это первый столбец в возможном выражении индекса с несколькими ключами? Раньше я использовал VFP OleDb, и у меня были довольно быстрые запросы ... даже в .Net 3.5 и 4. Однако для производства я настоятельно рекомендую параметризовать запросы. - person DRapp; 20.10.2015
comment
У меня есть указатель PRICE_N. В таблице несколько индексов, один из которых стоит PRICE_N. Этот индекс не кратный. Это просто индекс для одного столбца. Что касается параметризации запроса, я его тоже проверил - результат тот же - слишком медленно. - person Sergiy Vakshul; 20.10.2015
comment
@SergiyVakshul, еще одна последняя мысль ... это статическая база данных (то есть: не меняющий контент и фиксированные данные, которые не будут считываться / записываться веб-контентом, но могут быть заполнены другими средствами ... Если так, и вы делаете файлы table / cdx как разрешения ТОЛЬКО ДЛЯ ЧТЕНИЯ для запросов через Интернет / C #, оператору O / S не нужно делать какие-либо накладные расходы на любую возможную блокировку, даже если он в любом случае просто запрашивает. (также насколько велик это таблица) - person DRapp; 20.10.2015
comment
1 LIKE 10 - ЛОЖЬ, не так ли? LIKE неявно является ANSI ON и ведет себя так, как если бы вы использовали ==. - person Cetin Basoz; 20.10.2015

@Sergiy, Есть большая разница в том, что ты делаешь. Драйвер ODBC не существует для версий после VFP6 (а именно 2.5 или 2.6 был последним пакетом, содержащим драйвер ODBC). IOW ODBC поддерживает только механизм VFP6. OTOH VFPOLEDB поддерживает движок VFP9 (и все эти дополнительные возможности SQL с наворотами).

Между этими механизмами возникала проблема, из-за которой запросы к текстовым полям замедлялись: если кодовая страница ОС отличается от кодовой страницы таблицы и поиск выполняется по индексу, выражение которого имеет символьный тип. Тогда он не использует индекс, а выполняет сканирование таблицы. Эта «ошибка» появилась после первоначального выпуска VFP9 и не исправила AFAIK.

Согласно = vs like, like неявно является ANSI, поэтому ведет себя как ‹g> с использованием оператора == (точное совпадение). С =, если ANSI выключен, вы получаете частичные совпадения как истинные.

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
person Cetin Basoz    schedule 20.10.2015

@ Цетин Басоз,

Между этими механизмами возникала проблема, из-за которой запросы к текстовым полям замедлялись: если кодовая страница ОС отличается от кодовой страницы таблицы и поиск выполняется по индексу, выражение которого имеет символьный тип. Тогда он не использует индекс, а выполняет сканирование таблицы. Эта «ошибка» появилась после первоначального выпуска 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

Спасибо, Цетин Басоз. Комментарий был потрясающим :)

Несмотря на то, что мне не разрешено изменять кодовую страницу производственного файла DBF, по крайней мере, теперь я могу сбросить бремя с чувством облегчения, когда я знаю, что происходит.

person Sergiy Vakshul    schedule 20.10.2015