Как вставить 800000 записей в таблицу MS Access?

Мне нужно вставить 800000 записей в таблицу MS Access. Я использую Delphi 2007 и компоненты TAdoXxxx. Таблица содержит несколько целочисленных полей, одно поле с плавающей запятой и одно текстовое поле только с одним символом. Существует первичный ключ для одного из целочисленных полей (которое не является autoinc) и два индекса для другого целого числа и поля с плавающей запятой.

Вставка данных с использованием AdoTable.AppendRecord(...) занимает> 10 минут, что неприемлемо, поскольку это делается каждый раз, когда пользователь начинает использовать новую базу данных с программой. Я не могу предварительно заполнить таблицу, потому что данные поступают из другой базы данных (к которой нет доступа через ADO).

Мне удалось сократить примерно до 1 минуты, записав записи в текстовый файл, разделенный табуляцией, и используя объект tAdoCommand для выполнения.

insert into table (...) select * from [filename.txt] in "c:\somedir" "Text;HDR=Yes"

Но мне не нравятся накладные расходы.

Думаю, должен быть лучший способ.

РЕДАКТИРОВАТЬ:

Некоторая дополнительная информация:

  • MS Access был выбран потому, что он не требует дополнительной установки на целевой машине (компьютерах), а вся база данных содержится в одном файле, который можно легко скопировать.
  • Это однопользовательское приложение.
  • Данные будут вставлены только один раз и не изменятся в течение всего времени существования базы данных. Тем не менее, таблица содержит одно дополнительное поле, которое используется в качестве флага, чтобы указать, что соответствующая запись в другой базе данных была обработана пользователем.
  • Одна минута достаточно приемлема (до 3 минут тоже подойдет), и мое решение работает, но мне оно кажется слишком сложным, поэтому я подумал, что должен быть более простой способ сделать это.
  • После вставки данных производительность таблицы достаточно высока.
  • Когда я начал планировать/внедрять функцию работы программы с БД Access, таблица была не нужна. Это стало необходимо только позже, когда заказчик запросил другую функцию. (Разве это не всегда так?)

РЕДАКТИРОВАТЬ:

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


person dummzeuch    schedule 06.02.2009    source источник
comment
Насколько быстро вам это нужно? Похоже, вы получили это как можно быстрее, учитывая, что это заняло 10 минут, а теперь занимает всего 1   -  person Eppz    schedule 06.02.2009
comment
1 минута кажется вполне приемлемой для вставки 800000 записей.   -  person Ed S.    schedule 06.02.2009
comment
1 минута вполне приемлема, но хотелось бы обойтись без создания этого текстового файла.   -  person dummzeuch    schedule 06.02.2009
comment
Что не так с созданием текстового файла?   -  person Walter Mitty    schedule 09.02.2009
comment
Это просто неправильно. У меня есть данные в памяти, но для того, чтобы вставить их в базу данных, я должен сначала сохранить их в файл.   -  person dummzeuch    schedule 10.02.2009
comment
В SQLite вы должны добавить BEGIN; и добавить COMMIT; к нескольким операторам SQL. Это значительно ускоряет процесс. (Потому что он вставляет данные только один раз, а не для каждой записи.) Может быть, есть что-то подобное для доступа?   -  person Georg Schölly    schedule 14.09.2010


Ответы (16)


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

Итак, используйте этот файл, чтобы начать реальное заполнение — Access не будет запрашивать (или очень мало) дополнительного дискового пространства. Не помните, есть ли у MS Access способ автоматизировать это, но это может сильно помочь...

person Fabricio Araujo    schedule 14.09.2010
comment
Интересная идея. К сожалению, это не сработает для меня, потому что я понятия не имею, насколько большой будет эта таблица на самом деле (800000 записей были тестовым примером, который я использовал, фактический размер зависит от содержимого другой таблицы, которая недоступна, когда доступ к БД создан.) Может быть, я сделаю несколько тестов, чтобы увидеть, заполнять ли его, например, 500000 записей значительно ускоряют последующий импорт, чтобы окупить его. - person dummzeuch; 15.09.2010
comment
Мой опыт касается SQLServer, но другие механизмы баз данных (например, Firebird) не автоматически сжимают файлы баз данных по той же причине: избегая запроса нового выделения дискового пространства для базовой ОС, если это возможно. Даже операция восстановления в SQLServer работает с предварительным выделением дискового пространства. - person Fabricio Araujo; 15.09.2010
comment
Насколько ты улучшился, dummzeuch? - person Fabricio Araujo; 04.10.2010

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

Если вы настаиваете на том, чтобы вытащить его в базу данных, то 800 000 записей за 1 минуту — это более 13 000 в секунду. Я не думаю, что вы превзойдете это в MS Access.

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

person JosephStyons    schedule 06.02.2009
comment
Мне нужны данные внутри базы данных, потому что пользователь может скопировать их с одного компьютера на другой, чтобы продолжить работу с ними. Связывание потребовало бы, чтобы он также скопировал текстовый файл, а также сохранил путь к нему. Предполагаемые пользователи недостаточно разбираются в компьютерах для этого. - person dummzeuch; 06.02.2009
comment
Сколько времени потребуется, чтобы просто импортировать текстовый файл в виде таблицы (т. е. никуда его не вставлять, а просто импортировать как есть?) - person JosephStyons; 09.02.2009

Без индексов было бы быстрее. Можно ли добавить их после импорта?

В этой теме есть ряд предложений, которые могут представлять интерес Медленная запись на диск MSAccess

person Fionnuala    schedule 06.02.2009
comment
Да, я думал об этом, но некоторые испытания не показали особого улучшения производительности. - person dummzeuch; 06.02.2009

Как насчет того, чтобы пропустить текстовый файл и использовать ODBC или OLEDB для импорта непосредственно из исходной таблицы? Это означало бы изменение предложения FROM для использования имени исходной таблицы и соответствующей строки соединения в качестве части IN '' предложения FROM.

РЕДАКТИРОВАТЬ: На самом деле я вижу, что вы говорите, что исходным форматом является xBase, поэтому должна быть возможность использовать xBase ISAM, который является частью Jet, вместо необходимости использования ODBC или OLEDB. Это будет выглядеть примерно так:

INSERT INTO table (...) 
SELECT * 
FROM tablename IN 'c:\somedir\'[dBase 5.0;HDR=NO;IMEX=2;];

Возможно, вам придется настроить это — я только что взял строку подключения для связанной таблицы, указывающей на файл DBF, поэтому параметры могут немного отличаться.

person David-W-Fenton    schedule 06.02.2009
comment
Это может быть просто возможно, но это потребует довольно уродливого редизайна модуля (который в настоящее время не знает об исходной таблице, а имеет только интерфейс для получения данных). Я проверю это снова завтра. - person dummzeuch; 07.02.2009
comment
Я недостаточно знаю ваш код, чтобы понять, почему это может быть проблемой - поскольку вы выполняли INSERT в текстовом файле, кажется, вы просто пропустили бы создание текстового файла и заменили его прямой INSERT из таблица с SQL, написанным на лету, если вы не знаете имя таблицы до времени выполнения. - person David-W-Fenton; 07.02.2009

Как насчет альтернативной схемы...

Будет ли вариант сделать копию существующего файла базы данных Access, в котором есть эта таблица, которая вам нужна, а затем просто удалить все остальные данные, кроме этой одной большой таблицы (не знаю, есть ли в Access эквивалент чего-то вроде " обрезать таблицу» на сервере SQL)?

person pfunk    schedule 06.02.2009
comment
Если это не так, по крайней мере, удалить * из таблицы должно работать - person Hosam Aly; 07.02.2009

Я бы заменил MS Access другой базой данных, и в вашей ситуации я вижу, что Sqlite — лучший выбор, он не не требует установки на клиентскую машину, это очень быстрая база данных и одно из лучших встроенных решений для баз данных.

Вы можете использовать его в Delphi двумя способами:

  1. Вы можете загрузить Dll ядра базы данных с веб-сайта Sqlite и использовать бесплатный компонент Delphi для доступа к нему, например компоненты Delphi SQLite или SQLite4Delphi

  2. Используйте DISQLite3 со встроенным движком, и вы не не нужно распространять dll с вашим приложением, у них есть бесплатная версия ;-)

если вам все еще нужно использовать MS Access, попробуйте использовать TAdoCommand со статусом SQL Insert напрямую вместо использования TADOTable, это должно быть быстрее, чем использование TADOTable.Append;

person Mohammed Nasman    schedule 07.02.2009
comment
К сожалению, замена Access в настоящее время в проекте невозможна. - person dummzeuch; 07.02.2009
comment
Кроме того, вы уверены, что это ускорит импорт записей? - person Renaud Bompuis; 16.02.2009

Как кто-то упомянул, вы не будете импортировать 800 000 записей менее чем за минуту; это уже действительно быстро.

Однако вы можете пропустить раздражающий этап перевода в текстовый файл, если используете правильный метод (наборы записей DAO) для выполнения вставок. См. предыдущий вопрос, который я задал и ответил на StackOverflow: MS Access: почему ADODB.Recordset.BatchUpdate намного медленнее, чем Application.ImportXML?

Не используйте INSERT INTO даже с DAO; это медленно. Также не используйте ADO; это медленно. Но DAO + Delphi + Recordsets + создание экземпляра COM-объекта DbEngine напрямую (а не через объект Access.Application) даст вам большую скорость.

person apenwarr    schedule 14.09.2010
comment
Интересный подход. Я попробую позже (эта программа уже поставляется с методом импорта текстового файла). - person dummzeuch; 15.09.2010

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

Проблема в том, что Access обрабатывает внутреннюю оптимизацию записи, и на самом деле нет никакого способа контролировать это. Вероятно, вы достигли максимальной эффективности оператора INSERT. Для дополнительной скорости вам, вероятно, следует оценить, есть ли способ записи 800 000 записей в базу данных при каждом запуске приложения.

person Yes - that Jake.    schedule 06.02.2009
comment
ОП сказал, что это происходит только тогда, когда пользователь создает новую базу данных, а не каждый раз, когда приложение запускается. Это будет проблемой :) - person Ed S.; 06.02.2009
comment
Не каждый раз запускаю приложение. Это происходит только тогда, когда я начинаю использовать новую базу данных доступа, созданную с помощью другого инструмента. После того, как данные были добавлены, они не изменятся в течение всего срока службы базы данных. - person dummzeuch; 06.02.2009

Получите SQL Server Express (бесплатно) и подключитесь к нему из доступа к внешней таблице. . SQL Express намного быстрее, чем MS Access.

person Diodeus - James MacFarlane    schedule 06.02.2009
comment
В данном случае это не вариант. - person dummzeuch; 06.02.2009
comment
Получите встроенный SQL Server — установка не требуется, только DLL. ЕЩЕ ЛУЧШЕ, чем Access. - person TomTom; 14.09.2010

Я бы предварительно заполнил базу данных и передал бы им сам файл вместо того, чтобы заполнять существующую (но пустую) базу данных.

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

Когда пользователь запрашивает новую базу данных, заархивируйте MDB, передайте ее ему и откройте.

В качестве альтернативы вы можете найти код, который открывает и вставляет данные непосредственно в базы данных.

В качестве альтернативы, вы можете найти другой формат (кроме csv), доступ к которому может импортировать быстрее.

-Адам

person Adam Davis    schedule 06.02.2009
comment
Хм, ваше последнее предложение заставило меня задуматься: в настоящее время другой базой данных является dbase, и я думаю, что ADO также может читать dbase, поэтому я действительно мог бы напрямую использовать эту исходную таблицу в операторе вставки... - person dummzeuch; 07.02.2009
comment
Мне все больше и больше кажется, что исходные данные вообще не меняются. Если это так, то лучшим вариантом, я думаю, будет запись данных в файл базы данных доступа один раз, а затем просто копирование файла, когда пользователю нужна новая база данных. - person pfunk; 07.02.2009
comment
Нет, это не вариант. База данных доступа будет создана в соответствии с пользовательскими конфигурациями. Единственное, что всегда будет в этой базе данных, — это рассматриваемая таблица. Кроме того, есть несколько основных баз данных, и мне нужно скопировать из той, которая активна, когда пользователь начинает использовать базу данных доступа. - person dummzeuch; 07.02.2009
comment
О какой пользовательской конфигурации идет речь? Копирование базы данных просто пропустит шаг создания базы данных, вы просто скопируете существующий файл. Любые пользовательские конфигурации могут быть выполнены после копирования? - person Joel Gauvreau; 11.06.2009

Также проверьте, сколько времени потребуется для копирования файла. Это будет нижняя граница скорости записи данных. В db, подобном SQL, обычно требуется утилита массовой загрузки, чтобы приблизиться к этой скорости. Насколько мне известно, MS никогда не создавала инструмент для прямой записи в таблицы MS Access, как это делает bcp. Специализированные инструменты ETL также оптимизируют некоторые шаги, связанные со вставкой, например, то, как SSIS выполняет преобразования в памяти, DTS также имеет некоторые оптимизации.

person MatthewMartin    schedule 06.02.2009

Возможно, вы могли бы открыть набор записей ADO для таблицы с режимом блокировки adLockBatchOptimistic и CursorLocation adUseClient, записать все данные в набор записей, а затем выполнить пакетное обновление (rs.UpdateBatch).

person Tmdean    schedule 06.02.2009

Если он исходит из базы данных, вы можете просто скопировать файлы данных и индекса и прикрепить их напрямую без загрузки? Должно быть довольно эффективно (от людей, которые приносят вам FoxPro). Я полагаю, что он также будет использовать существующие индексы.

По крайней мере, это должен быть довольно эффективный импорт одной команды.

person dkretz    schedule 06.02.2009
comment
Нет, я не могу. новая таблица фактически содержит один дополнительный столбец, который я заполняю и запрашиваю во время более поздней операции. - person dummzeuch; 07.02.2009

насколько 800 000 записей меняются от одного создания к другому? Можно ли предварительно заполнить записи, а затем просто обновить те, которые изменились во внешней базе данных при создании новой базы данных?

Это может позволить вам быстрее создать новый файл базы данных.

person Toby Allen    schedule 07.02.2009
comment
Все эти записи могут измениться, потому что возможно (даже вероятно), что пользователь переключился на другую основную базу данных. - person dummzeuch; 07.02.2009

Как быстро вращается ваш диск? Если это 7200 об/мин, то 800 000 строк за 3 минуты по-прежнему составляют 37 строк на оборот диска. Не думаю, что у тебя получится намного лучше.

Между тем, если цель состоит в том, чтобы упростить процесс, как насчет ссылки на таблицу?

Вы говорите, что не можете получить доступ к исходной базе данных через ADO. Можете ли вы настроить ссылку таблицы в MS Access на таблицу или представление в исходной базе данных? Затем простой запрос на добавление из ссылки на таблицу скопирует данные из исходной базы данных в целевую базу данных для вас. Я не уверен, но я думаю, что это будет довольно быстро.

Если вы не можете настроить ссылку на таблицу до времени выполнения, возможно, вы могли бы создать ссылку на таблицу программно через ADO, затем создать запрос на добавление программно, а затем вызвать запрос на добавление.

person Walter Mitty    schedule 09.02.2009
comment
И база данных доступа, и главная база данных могут изменяться из-за действий пользователя, поэтому единственный способ создать ссылку внутри базы данных доступа — во время выполнения. Пока я буду придерживаться решения с текстовым файлом, потому что оно достаточно быстрое и простое в реализации, и я не знаю, как создавать ссылки через ADO. - person dummzeuch; 10.02.2009

Привет. Лучший способ - массовая вставка из txt-файла, поскольку они сказали, что вы должны вставить свои записи в txt-файл, а затем массово вставить txt-файл в таблицу, время должно быть менее 3 секунд.

person tiphooo    schedule 14.09.2010