Насколько быстро SQLite по сравнению с Microsoft Access MDB?

В настоящее время я думаю о замене использования баз данных Microsoft Jet MDB в однопользовательском приложении .NET С# Windows Forms с помощью базы данных SQlite.

Моя цель - снизить требования к установке, такие как драйверы Jet, и некоторые неприятные ошибки при повреждении установки Jet (у нас есть клиенты, которые время от времени сообщают об этих ошибках).

Мой вопрос относительно производительности:

Есть ли какие-либо тесты производительности, сравнивающие MDB и SQLite на довольно небольших наборах данных?

Или есть ли разработчики, которые уже сделали этот шаг и могут рассказать некоторые истории из своего собственного опыта?

(Я работаю в Интернете уже несколько часов без успеха)

Обновление

Несмотря на то, что база данных не содержит столько записей и таблиц, я думаю, что производительность по-прежнему остается проблемой, так как к данным обращаются довольно часто.

Приложение представляет собой так называемую "Систему рабочего стола CMS", которая отображает HTML-страницы; во время рендеринга выполняется довольно много данных, и выполняется большое количество SQL-запросов.

Обновление 2

Только что нашел эту документацию, которая показывает некоторые сравнения скорости, к сожалению, не с MDB, насколько я могу видеть.

Обновление 3

По запросу некоторые цифры:

  • ок. 30 таблиц в базе данных.
  • Большинство таблиц со 100 записями.
  • ок. 5 таблиц с обычно от нескольких до нескольких тысяч записей.
  • Большой файл MDB будет около 60 МБ.

Обновление 4

Просто перефразируйте: у меня нет проблем с производительностью с текущей реализацией MDB. Я задаю этот вопрос, чтобы понять, будет ли производительность быть равной (или лучше) при использовании SQLite вместо MDB.

Ответы

Ответ 1

Более 4 лет спустя я фактически провел небольшой (возможно, несколько наивный) тест сравнения производительности между MDB и SQLite.

Я также добавил больше баз данных.

Базы данных, которые я тестировал

  • SQL Server Express 2014 на том же локальном ПК и локальном SSD-диске, что и тестовое приложение.
  • SQL Server Express 2014 на сервере в гигабитной локальной сети.
  • SQL Server Compact (CE) на том же локальном ПК и локальном диске SSD, что и тестовое приложение.
  • Microsoft Access MDB/Jet на том же локальном ПК и локальном SSD-диске, что и тестовое приложение.
  • Microsoft SQLite на том же локальном ПК и локальном SSD-диске, что и тестовое приложение.
  • Microsoft VistaDB 5 на том же локальном ПК и локальном SSD-диске, что и тестовое приложение.

Поскольку некоторые базы данных не поддерживают пул соединений, я провел два теста:

  1. Один тест с закрытием соединения как можно скорее через блок using.
  2. Еще один тест с постоянно открытым подключением к каждой базе данных на весь срок службы приложения

Результаты теста при немедленном закрытии соединения

  • SQL Express, работающий локально, был самым быстрым.
  • SQL Express в нашей локальной сети оказался на второй позиции.
  • SQL Compact Edition (CE) был намного быстрее, чем SQLite и Jet/MDB.
  • Jet/MDB был немного быстрее, чем SQLite, и намного медленнее, чем SQL CE.
  • SQLite был немного медленнее, чем Jet/MDB.
  • VistaDB 5 была самой медленной базой данных в моем тесте.

Результаты теста при открытых соединениях

Результаты довольно похожи на результаты при немедленном закрытии соединения.

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

  • SQL Express, работающий локально, был самым быстрым.
  • SQL Express в нашей локальной сети оказался на второй позиции.
  • SQL Compact Edition (CE) был намного быстрее, чем SQLite и Jet/MDB.
  • Jet/MDB был немного быстрее, чем SQLite, и намного медленнее, чем SQL CE.
  • SQLite был немного медленнее, чем Jet/MDB.
  • VistaDB 5 была самой медленной базой данных в моем тесте.

Подробный вывод моего тестового приложения при немедленном закрытии соединения

1.: 1 x DELETE FROM Tabelle1 (Closing connections):
- SQL Express local : 00:00:00.1723705
- SQL Express remote: 00:00:00.2093229
- SQL CE            : 00:00:00.3141897
- MS Access         : 00:00:00.3854029
- SQLite            : 00:00:00.4639365
- VistaDB           : 00:00:00.9699047

2.: 1 x INSERT INTO Tabelle1 (Name1, Wert1) VALUES ({LOOPCTR}, '{LOOPCTR}') (Closing connections):
- SQL Express local : 00:00:00.0039836
- SQL Express remote: 00:00:00.0062002
- SQL CE            : 00:00:00.0432679
- MS Access         : 00:00:00.0817834
- SQLite            : 00:00:00.0933030
- VistaDB           : 00:00:00.1200426

3.: 10 x INSERT INTO Tabelle1 (Name1, Wert1) VALUES ({LOOPCTR}, '{LOOPCTR}') (Closing connections):
- SQL Express local : 00:00:00.0031593
- SQL Express remote: 00:00:00.0142514
- SQL CE            : 00:00:00.3724224
- MS Access         : 00:00:00.7474003
- SQLite            : 00:00:00.8818905
- VistaDB           : 00:00:00.9342783

4.: 100 x INSERT INTO Tabelle1 (Name1, Wert1) VALUES ({LOOPCTR}, '{LOOPCTR}') (Closing connections):
- SQL Express local : 00:00:00.0242817
- SQL Express remote: 00:00:00.1124771
- SQL CE            : 00:00:03.6239390
- MS Access         : 00:00:07.3752378
- SQLite            : 00:00:08.6489843
- VistaDB           : 00:00:09.0933903

5.: 1000 x INSERT INTO Tabelle1 (Name1, Wert1) VALUES ({LOOPCTR}, '{LOOPCTR}') (Closing connections):
- SQL Express local : 00:00:00.2735537
- SQL Express remote: 00:00:01.2657006
- SQL CE            : 00:00:36.2335727
- MS Access         : 00:01:13.8782439
- SQLite            : 00:01:27.1783328
- VistaDB           : 00:01:32.0760340

6.: 1 x SELECT * FROM Tabelle1 (Closing connections):
- SQL Express local : 00:00:00.0520670
- SQL Express remote: 00:00:00.0570562
- SQL CE            : 00:00:00.1026963
- MS Access         : 00:00:00.1646635
- SQLite            : 00:00:00.1785981
- VistaDB           : 00:00:00.2311263

7.: 10 x SELECT * FROM Tabelle1 (Closing connections):
- SQL Express local : 00:00:00.0183055
- SQL Express remote: 00:00:00.0501115
- SQL CE            : 00:00:00.3235680
- MS Access         : 00:00:00.7119203
- SQLite            : 00:00:00.7533361
- VistaDB           : 00:00:00.9804508

8.: 100 x SELECT * FROM Tabelle1 (Closing connections):
- SQL Express local : 00:00:00.1787837
- SQL Express remote: 00:00:00.4321814
- SQL CE            : 00:00:03.0401779
- MS Access         : 00:00:06.8338598
- SQLite            : 00:00:07.2000139
- VistaDB           : 00:00:09.1889217

9.: 1000 x SELECT * FROM Tabelle1 (Closing connections):
- SQL Express local : 00:00:01.6112566
- SQL Express remote: 00:00:03.9542611
- SQL CE            : 00:00:29.1209991
- MS Access         : 00:01:07.2309769
- SQLite            : 00:01:10.3167922
- VistaDB           : 00:01:31.4312770

10.: 1 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID ORDER BY a.ID (Closing connections):
- SQL Express local : 00:00:00.0029406
- SQL Express remote: 00:00:00.0088138
- SQL CE            : 00:00:00.0498847
- MS Access         : 00:00:00.0893892
- SQLite            : 00:00:00.0929506
- VistaDB           : 00:00:00.2575795

11.: 10 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID ORDER BY a.ID (Closing connections):
- SQL Express local : 00:00:00.0174026
- SQL Express remote: 00:00:00.0400797
- SQL CE            : 00:00:00.3408818
- MS Access         : 00:00:00.7314978
- SQLite            : 00:00:00.7653330
- VistaDB           : 00:00:01.9565675

12.: 100 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID ORDER BY a.ID (Closing connections):
- SQL Express local : 00:00:00.1565402
- SQL Express remote: 00:00:00.3787208
- SQL CE            : 00:00:03.3516629
- MS Access         : 00:00:07.2521126
- SQLite            : 00:00:07.5618047
- VistaDB           : 00:00:19.5181391

13.: 1000 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID ORDER BY a.ID (Closing connections):
- SQL Express local : 00:00:01.5686470
- SQL Express remote: 00:00:03.7414669
- SQL CE            : 00:00:35.3944204
- MS Access         : 00:01:14.6872377
- SQLite            : 00:01:17.9964955
- VistaDB           : 00:03:18.1902279

14.: 1 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID WHERE a.ID < 100 OR a.ID > 300 ORDER BY a.ID (Closing connections):
- SQL Express local : 00:00:00.0053295
- SQL Express remote: 00:00:00.0089722
- SQL CE            : 00:00:00.0395485
- MS Access         : 00:00:00.0797776
- SQLite            : 00:00:00.0833477
- VistaDB           : 00:00:00.2554930

15.: 10 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID WHERE a.ID < 100 OR a.ID > 300 ORDER BY a.ID (Closing connections):
- SQL Express local : 00:00:00.0168467
- SQL Express remote: 00:00:00.0552233
- SQL CE            : 00:00:00.3929877
- MS Access         : 00:00:00.7886399
- SQLite            : 00:00:00.8209904
- VistaDB           : 00:00:02.1248734

16.: 100 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID WHERE a.ID < 100 OR a.ID > 300 ORDER BY a.ID (Closing connections):
- SQL Express local : 00:00:00.1705345
- SQL Express remote: 00:00:00.3969228
- SQL CE            : 00:00:03.4886826
- MS Access         : 00:00:07.4564258
- SQLite            : 00:00:07.7828646
- VistaDB           : 00:00:20.4092926

17.: 1000 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID WHERE a.ID < 100 OR a.ID > 300 ORDER BY a.ID (Closing connections):
- SQL Express local : 00:00:01.6237424
- SQL Express remote: 00:00:03.9816212
- SQL CE            : 00:00:35.1441759
- MS Access         : 00:01:14.7739758
- SQLite            : 00:01:17.9477049
- VistaDB           : 00:03:24.0049633

Подробный вывод моего тестового приложения при сохранении открытых соединений

1.: 1 x DELETE FROM Tabelle1 (keeping connection open):
- SQL Express local : 00:00:00.0426930
- SQL Express remote: 00:00:00.0546357
- SQL CE            : 00:00:00.0786765
- MS Access         : 00:00:00.0909099
- SQLite            : 00:00:00.1101572
- VistaDB           : 00:00:00.4637726

2.: 1 x INSERT INTO Tabelle1 (Name1, Wert1) VALUES ({LOOPCTR}, '{LOOPCTR}') (keeping connection open):
- SQL Express local : 00:00:00.0030936
- SQL Express remote: 00:00:00.0051136
- SQL CE            : 00:00:00.0054226
- MS Access         : 00:00:00.0074847
- SQLite            : 00:00:00.0154474
- VistaDB           : 00:00:00.0373701

3.: 10 x INSERT INTO Tabelle1 (Name1, Wert1) VALUES ({LOOPCTR}, '{LOOPCTR}') (keeping connection open):
- SQL Express local : 00:00:00.0023271
- SQL Express remote: 00:00:00.0109913
- SQL CE            : 00:00:00.0119872
- MS Access         : 00:00:00.0152531
- SQLite            : 00:00:00.1131698
- VistaDB           : 00:00:00.1261859

4.: 100 x INSERT INTO Tabelle1 (Name1, Wert1) VALUES ({LOOPCTR}, '{LOOPCTR}') (keeping connection open):
- SQL Express local : 00:00:00.0201695
- SQL Express remote: 00:00:00.0888872
- SQL CE            : 00:00:00.0966017
- MS Access         : 00:00:00.1256167
- SQLite            : 00:00:01.3632978
- VistaDB           : 00:00:01.9422151

5.: 1000 x INSERT INTO Tabelle1 (Name1, Wert1) VALUES ({LOOPCTR}, '{LOOPCTR}') (keeping connection open):
- SQL Express local : 00:00:00.1693362
- SQL Express remote: 00:00:00.9181297
- SQL CE            : 00:00:01.0366334
- MS Access         : 00:00:01.2794199
- SQLite            : 00:00:13.9398816
- VistaDB           : 00:00:19.8319476

6.: 1 x SELECT * FROM Tabelle1 (keeping connection open):
- SQL Express local : 00:00:00.0481500
- SQL Express remote: 00:00:00.0507066
- SQL CE            : 00:00:00.0738698
- MS Access         : 00:00:00.0911707
- SQLite            : 00:00:00.1012425
- VistaDB           : 00:00:00.1515495

7.: 10 x SELECT * FROM Tabelle1 (keeping connection open):
- SQL Express local : 00:00:00.0157947
- SQL Express remote: 00:00:00.0692206
- SQL CE            : 00:00:00.0898558
- MS Access         : 00:00:00.1196514
- SQLite            : 00:00:00.1400944
- VistaDB           : 00:00:00.3227485

8.: 100 x SELECT * FROM Tabelle1 (keeping connection open):
- SQL Express local : 00:00:00.1517498
- SQL Express remote: 00:00:00.3399897
- SQL CE            : 00:00:00.5497382
- MS Access         : 00:00:00.8619646
- SQLite            : 00:00:01.0463369
- VistaDB           : 00:00:02.8607334

9.: 1000 x SELECT * FROM Tabelle1 (keeping connection open):
- SQL Express local : 00:00:01.5042900
- SQL Express remote: 00:00:03.8431985
- SQL CE            : 00:00:05.9075477
- MS Access         : 00:00:09.2642402
- SQLite            : 00:00:11.4427914
- VistaDB           : 00:00:30.8470936

10.: 1 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID ORDER BY a.ID (keeping connection open):
- SQL Express local : 00:00:00.0033803
- SQL Express remote: 00:00:00.0062499
- SQL CE            : 00:00:00.0141105
- MS Access         : 00:00:00.0188573
- SQLite            : 00:00:00.0208236
- VistaDB           : 00:00:00.1796513

11.: 10 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID ORDER BY a.ID (keeping connection open):
- SQL Express local : 00:00:00.0168644
- SQL Express remote: 00:00:00.0377185
- SQL CE            : 00:00:00.1121558
- MS Access         : 00:00:00.1599104
- SQLite            : 00:00:00.1799435
- VistaDB           : 00:00:01.4042534

12.: 100 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID ORDER BY a.ID (keeping connection open):
- SQL Express local : 00:00:00.1547275
- SQL Express remote: 00:00:00.3692526
- SQL CE            : 00:00:01.1215470
- MS Access         : 00:00:01.5577172
- SQLite            : 00:00:01.7519790
- VistaDB           : 00:00:14.5687575

13.: 1000 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID ORDER BY a.ID (keeping connection open):
- SQL Express local : 00:00:01.4992800
- SQL Express remote: 00:00:03.7601806
- SQL CE            : 00:00:11.1738426
- MS Access         : 00:00:15.8112902
- SQLite            : 00:00:17.8045042
- VistaDB           : 00:02:21.4492368

14.: 1 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID WHERE a.ID < 100 OR a.ID > 300 ORDER BY a.ID (keeping connection open):
- SQL Express local : 00:00:00.0048145
- SQL Express remote: 00:00:00.0076790
- SQL CE            : 00:00:00.0152074
- MS Access         : 00:00:00.0204568
- SQLite            : 00:00:00.0229056
- VistaDB           : 00:00:00.2091614

15.: 10 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID WHERE a.ID < 100 OR a.ID > 300 ORDER BY a.ID (keeping connection open):
- SQL Express local : 00:00:00.0156564
- SQL Express remote: 00:00:00.0377571
- SQL CE            : 00:00:00.1138433
- MS Access         : 00:00:00.1598932
- SQLite            : 00:00:00.1793267
- VistaDB           : 00:00:01.4667061

16.: 100 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID WHERE a.ID < 100 OR a.ID > 300 ORDER BY a.ID (keeping connection open):
- SQL Express local : 00:00:00.1512625
- SQL Express remote: 00:00:00.4658652
- SQL CE            : 00:00:01.2441809
- MS Access         : 00:00:01.7224126
- SQLite            : 00:00:01.9297231
- VistaDB           : 00:00:14.9351318

17.: 1000 x SELECT a.* FROM Tabelle1 a LEFT JOIN Tabelle1 b ON a.ID=b.ID WHERE a.ID < 100 OR a.ID > 300 ORDER BY a.ID (keeping connection open):
- SQL Express local : 00:00:01.5223833
- SQL Express remote: 00:00:03.9885174
- SQL CE            : 00:00:11.8356048
- MS Access         : 00:00:16.5977939
- SQLite            : 00:00:18.6504260
- VistaDB           : 00:02:26.0513056

Обновление 1, апрель 2019 г.

Я провел несколько тестов, сравнивая Microsoft Access MDB с LiteDB, встроенной базой данных для .NET.

Итак, еще раз несколько довольно наивное сравнение, но я все же хотел получить ощущение.

Этот код делает 1000 операций чтения и записи и приводит к следующим значениям:

Access             of 1000 WRITE iterations took 00:00:39.6488351.
LiteDB             of 1000 WRITE iterations took 00:00:01.6596646.
LiteDB (in-memory) of 1000 WRITE iterations took 00:00:00.1617220.
Access             of 1000 READ  iterations took 00:00:48.8517302.
LiteDB             of 1000 READ  iterations took 00:00:00.0082401.
LiteDB (in-memory) of 1000 READ  iterations took 00:00:00.0097933.

Так что в моем сценарии LiteDB был намного быстрее, чем Access.

Обновление 2, апрель 2019 г.

Я также использовал свой оригинальный код для VistaDB 6 Beta 1 по сравнению с VistaDB 5.

У меня очень похожие результаты по скорости. Бета-версия VistaDB 6 была немного медленнее по сравнению с VistaDB 5, скорее всего потому, что это была отладочная сборка.

В заключение я не вижу значительных улучшений производительности в моем сценарии между VistaDB 5 и VistaDB 6 Beta 1. Мне придется повторить попытку с финальной версией VistaDB 6.

Ответ 2

Если вы решите провести собственное тестовое тестирование, я предлагаю эту процедуру для экспорта таблиц Jet в CSV файлы. Затем вы можете импортировать их в свою базу данных SQLite.

Public Sub DumpTablesAsCsv()
    Dim db As DAO.Database
    Dim tdf As DAO.TableDef
    Dim strCsvFile As String
    Dim strFolder As String
    Dim strTable As String

    strFolder = CurrentProject.Path & Chr(92)
    Set db = CurrentDb
    For Each tdf In db.TableDefs
        strTable = tdf.Name
        If Not (strTable Like "MSys*" Or strTable Like "~*") Then
            strCsvFile = strFolder & strTable & ".csv"
            DoCmd.TransferText acExportDelim, , strTable, _
                strCsvFile, HasFieldNames:=True
        End If
    Next tdf
    Set tdf = Nothing
    Set db = Nothing
End Sub

Ответ 3

На самом деле, я не уверен, что вы действительно задаете правильный вопрос здесь.

Мне кажется, что вы ищете решение, изменяя свои инструменты, а не меняя свой дизайн и свои подходы. Фактически, для большинства операций реактивный двигатель доступа значительно быстрее, чем что-то вроде oracle, mySQL или SQL-сервера. Причина в том, что другие системы представляют собой огромную массу серверных систем, которые имеют соединения сокетов с сервером. У них есть уровни обработки транзакций. Существует, вероятно, 500 дополнительных уровней программного обеспечения и систем между вами и фактическими данными, которые находятся на жестком диске.

Контрастируйте это, чтобы получить доступ к тому, что по сути является в программе обработки (а не как запущенная служба). Вы не подключаетесь к файлам данных Access через некоторые TCP/IP-соединения, как в случае с серверными системами (на самом деле большинство этих серверных систем заставляют вас подключаться через сетевой и сетевой уровень, даже на вашем локальном компьютере, и меньше вы используете локальную память, предполагая, что опция доступна).

JET (механизм доступа к базе данных) не является сервисом и просто очищает файл от жесткого диска и отображает результаты. Это очистка данных с диска происходит с той же скоростью, что и сервер oracle или SQL, и все эти другие системы (здесь мы используем те же машины и оборудование). Тем не менее, в этих других системах по-прежнему имеется еще 500, возможно, еще 1000 дополнительных уровней кода и программных и сетевых подключений, а также огромное количество таких функций, как безопасность пользователей и т.д. Все это существенно замедляет процесс получения данных на диске большим количеством.

Теперь, если вы говорите о подключении по сети какого-то типа, то эти серверные системы лучше, потому что вы хотите, чтобы вся обработка и все, что было majic, произошло до того, как все данные начнут течь по сетевому каналу.

Однако в вашем сценарии сервер и машина являются одними и теми же. Поэтому совершенно необходимо устранить массу огромного контекста тысяч дополнительных слоев программного обеспечения. Как я уже указывал, и эти типы сценариев, реактивный самолет может составлять 50% или даже удваивать скорость серверных систем, таких как MySql или Oracle.

Доступ может объединяться, классифицироваться и суммироваться для 150 000 записей в течение секунды, а также при подключении нескольких таблиц.

Теперь, с другой стороны, в любой из этих систем, как правило, большие накладные расходы, заключается в том, чтобы открыть соединение с конкретной таблицей. Фактически, время, необходимое для открытия таблицы, - это стоимость 30 000 записей для передачи. Таким образом, это означает, что вы хотите, чтобы ваш код и использование этих таблиц не требовали открытия новой таблицы (особенно в некоторых типах циклов кода. Иными словами, даже в местах многократного выполнения команды insert SQL, вы "намного лучше открыть набор записей, а затем сделать вставки таким образом, так как тогда вы больше не используете команды SQL, и для каждой вставки строки вы не выполняете отдельный синтаксический разбор текста в этом sql ( это может дать вам примерно 100-кратное увеличение производительности при использовании этого доступа - другими словами, часто цитируемый совет заключается в том, что использование команд SQL быстрее, чем открытие набора записей, является абсолютно неправильным).

Это означает, что если вы испытываете некоторый замедление здесь, я бы посмотрел на ваш код и проекты, и убедитесь, что наборы записей и наборы данных неоднократно открываются и закрываются. Вы не должны испытывать какой-либо заметной задержки в ваших операциях с данными, учитывая крошечный размер файлов, которые вы упомянули здесь.

Чтобы быть справедливым, sqlLITE также (я считаю) не-серверная версия MySql, и большинство преимуществ, упомянутых выше, также будут применяться. Но опять же, ваша шея бутылки не будет сильно отличаться в каждом случае, и поэтому мы возвращаемся к проблемам здесь.

Другими словами, вы лаяете неправильное дерево, а разработчик, который ищет изменения в своих инструментах для исправления производительности, просто ищет исправление, обвиняя инструменты, когда в большинстве случаев проблема заключается в принятых решениях.

Ответ 4

Jet 4.0, DAO, MDAC и ADO были включены как часть ОС Windows с Windows 2000. Таким образом, нет необходимости распространять любые "драйверы" Jet с вашим приложением.