Каков самый быстрый способ загрузки большого CSV-файла в основные данные
вывод
Думаю, проблема решена.
Похоже, проблема не имела ничего общего с методологией, но что XCode не очистил проект правильно между сборками.
Похоже, что после всех этих тестов файл sqlite, который использовался, все еще был самым первым, который не был индексирован......
Остерегайтесь XCode 4.3.2, у меня нет ничего, кроме проблем с чистой не очисткой или добавлением файлов в проект, которые не добавляются автоматически ресурсы пакета...
Спасибо за разные ответы..
обновление 3
Поскольку я приглашаю кого - либо просто попробовать те же шаги, чтобы увидеть, получают ли они те же результаты, позвольте мне подробно рассказать, что я сделал:
Я начинаю с пустого проекта
Я определил datamodel с одним объектом, 3 атрибутами (2 строки, 1 float)
Первая строка индексируется
В did finishLaunchingWithOptions, я призываю:
[self performSelectorInBackground:@selector(populateDB) withObject:nil];
В код для populateDb приведен ниже:
-(void)populateDB{
NSLog(@"start");
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
NSManagedObjectContext *context;
if (coordinator != nil) {
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:coordinator];
}
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"input" ofType:@"txt"];
if (filePath) {
NSString * myText = [[NSString alloc]
initWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:nil];
if (myText) {
__block int count = 0;
[myText enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) {
line=[line stringByReplacingOccurrencesOfString:@"t" withString:@" "];
NSArray *lineComponents=[line componentsSeparatedByString:@" "];
if(lineComponents){
if([lineComponents count]==3){
float f=[[lineComponents objectAtIndex:0] floatValue];
NSNumber *number=[NSNumber numberWithFloat:f];
NSString *string1=[lineComponents objectAtIndex:1];
NSString *string2=[lineComponents objectAtIndex:2];
NSManagedObject *object=[NSEntityDescription insertNewObjectForEntityForName:@"Bigram" inManagedObjectContext:context];
[object setValue:number forKey:@"number"];
[object setValue:string1 forKey:@"string1"];
[object setValue:string2 forKey:@"string2"];
NSError *error;
count++;
if(count>=1000){
if (![context save:&error]) {
NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}
count=0;
}
}
}
}];
NSLog(@"done importing");
NSError *error;
if (![context save:&error]) {
NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
}
}
}
NSLog(@"end");
}
все остальное-код основных данных по умолчанию, ничего не добавлено.
Я запускаю это в симуляторе.
I go to ~/Library/поддержка приложений/iPhone Simulator/5.1/приложения/ / документы
Существует файл sqlite, который генерируется
Я беру это и копирую в своем узле
Я комментирую вызов populateDb
Я редактирую persistentStoreCoordinator скопировать файл SQLite из пакета документы при первом запуске
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
@synchronized (self)
{
if (__persistentStoreCoordinator != nil)
return __persistentStoreCoordinator;
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"myProject" ofType:@"sqlite"];
NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent: @"myProject.sqlite"];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:storePath])
{
if ([[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:storePath error:&error])
NSLog(@"Copied starting data to %@", storePath);
else
NSLog(@"Error copying default DB to %@ (%@)", storePath, error);
}
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}
}
Я удаляю приложение из симулятора, я проверяю, что ~ / библиотека / поддержка приложений / iPhone Simulator / 5.1 / приложения / теперь удалены
я перестраиваю и запускаю снова
Как и ожидалось, файл sqlite копируется в ~ / Library / Application Support/iPhone Simulator/5.1/Applications/ / Documents
однако размер файла меньше, чем в связке, значительно!
кроме того, делает простой запрос с таким предикатом, как этот предикат = [NSPredicate predicateWithFormat:@ " string1 == %@", string1]; ясно показывает, что string1 больше не индексируется
После этого я создаю новую версию datamodel с бессмысленным обновлением, просто чтобы заставить легкую миграцию
При запуске на симуляторе миграция занимает несколько секунд, база данных удваивается по размеру, и один и тот же запрос теперь возвращается менее чем за секунду протокол.
Это решило бы мою проблему, заставило бы миграцию, но эта же миграция занимает 3 минуты на iPad и происходит на переднем плане.
Поэтому сейчас я нахожусь там, где я сейчас, лучшим решением для меня было бы предотвратить удаление индексов, любое другое импортирующее решение во время запуска просто занимает слишком много времени.
Дайте мне знать, если вам понадобятся дополнительные разъяснения...
обновление 2
Так что лучший результат у меня до сих пор есть семя база данных core data с файлом sqlite создается из быстрого инструмента с аналогичной моделью данных, но без индексов, установленных при создании файла sqlite. Затем я импортирую этот файл sqlite в приложение core data с набором индексов и разрешаю легкую миграцию. Для записи 2 миллионов на новом iPad эта миграция занимает 3 минуты. Окончательное приложение должно иметь в 5 раз больше записей,поэтому мы все еще смотрим на долгое время обработки.
Если я пойду этим путем, новый вопрос будет ли: может ли легкая миграция выполняться в фоновом режиме?
обновление
Мой вопрос не в том, как создать инструмент для заполнения базы данных Core Data, а затем импортировать файл sqlite в мое приложение.
Я знаю как это сделать, я уже делал это бесчисленное количество раз.
но до сих пор я не понимал, что такой метод может иметь какой-то побочный эффект: в моем случае индексированный атрибут в результирующей базе данных явно получил "unindexed" при импорте sqlite подай сюда.
Если вы смогли проверить, что любые индексированные данные все еще индексируются после такой передачи, мне интересно знать, как вы действуете, или иначе, какая была бы лучшая стратегия для эффективного заполнения такой базы данных.
Оригинал
у меня есть большой CSV-файл (миллионы строк) с 4 столбцами, строками и поплавки.
Это для приложения iOS.
Мне нужно, чтобы это было загружено в основные данные в первый раз, когда приложение нагруженный.
Приложение в значительной степени не работает до тех пор, пока данные не будут доступны, поэтому время загрузки имеет значение, поскольку первый пользователь явно не хочет, чтобы приложение загружалось за 20 минут до его запуска.
Прямо сейчас, мой текущий код занимает 20 минут на новом iPad для обработки файла csv 2 миллионов строк.
Я использую фоновый контекст, чтобы не блокировать пользовательский интерфейс и сохранять контекст каждые 1000 записей
Первой моей идеей было создать база данных на симуляторе, затем скопировать / вставить его в папку документа при первом запуске, так как это общий неофициальный способ заполнения большой базы данных. К сожалению, индексы, похоже, не выдерживают такой передачи, и хотя база данных была доступна всего через несколько секунд, производительность ужасна, потому что мои индексы были потеряны. Я уже отправил вопрос об индексах, но, похоже, на это нет хорошего ответа.
Так что я ищу, либо:
- способ повысить производительность при загрузке миллионов записей в core data
- если база данных предварительно загружена и перемещена при первом запуске, способ сохранить мои индексы
- рекомендации по обработке такого сценария. Я не помню, чтобы какое-либо приложение требовало от меня ждать x минут перед первым использованием (но, возможно, ежедневно, и это был ужасный опыт).
- любой творческий способ заставить пользователя ждать без него понимая это: импорт фона во время прохождения учебника и т. д...
- Не Используя Основные Данные?
- ...
2 ответов
предварительно создайте свою базу данных с помощью автономного приложения (скажем, утилиты командной строки), написанного в Cocoa, которое работает на OS X и использует ту же основную структуру данных, что и iOS. Вам не нужно беспокоиться о "выживании индексов" или о чем-либо еще-выходные данные являются основными данными .файл базы данных sqlite, непосредственно и немедленно используемый приложением iOS.
пока вы можете сделать генерацию БД оффлайн, это лучшее решение на сегодняшний день. Я успешно использовал эту технику, чтобы предварительно сгенерированные базы данных для развертывания iOS. Проверьте мои предыдущие вопросы/ответы для немного более подробно.
Я только начинаю с SQLite, и мне нужно интегрировать БД в одно из моих приложений, которое будет иметь много индексированных данных в базе данных SQLite. Я надеялся, что смогу сделать какой-то метод, где я мог бы вставить свою информацию в файл SQLite и добавить этот файл в свой проект. После обнаружения и чтения вашего вопроса, предоставленного ответа и многочисленных комментариев я решил проверить источник SQLite, чтобы узнать, могу ли я сделать орел или решку этой проблемы.
мой первоначальная мысль заключалась в том, что реализация iOS SQLite на самом деле выбрасывает ваши индексы. Причина в том, что вы изначально создаете свой индекс БД в системе x86/x64. IOS-это процессор ARM, и номера обрабатываются по-разному. Если вы хотите, чтобы ваши индексы были быстрыми, вы должны генерировать их таким образом, чтобы они были оптимизированы для процессора, в котором они будут искать.
поскольку SQLite предназначен для нескольких платформ, он сделает так, чтобы удалить любые индексы, которые были созданы в другой архитектуре и перестроены. Однако, поскольку никто не хочет ждать перестроения индекса при первом доступе, разработчики SQLite, скорее всего, решили просто удалить индекс.
после копания в коде SQLite я пришел к выводу, что это скорее всего происходит. Если бы не причина архитектуры процессора, я нашел код (см. analyze.c
и другие мета-информации sqliteint.h
) где индексы удаляются, если они были созданы в неожиданном контексте. Моя догадка заключается в том, что контекст, который управляет этим процессом,-это то, как базовая структура данных b-tree была построена для существующего ключа. Если текущий экземпляр SQLite не может использовать ключ, он удаляет его.
стоит отметить, что симулятор iOS - это просто симулятор. Это не эмулятор, оборудования. Таким образом, ваше приложение работает на псевдо-iOS устройстве, работающем на процессор x86/x64.
когда ваше приложение и SQLite DB загружаются на устройство iOS, загружается ARM-скомпилированный вариант, который также ссылается на скомпилированные библиотеки ARM в iOS. Я не мог найти конкретный код ARM, связанный с SQLite, поэтому я предполагаю, что Apple пришлось изменить его на свой костюм. Это тоже может быть частью проблемы. Это может быть не проблема с кодом root-SQLite, это может быть проблема с вариантом, скомпилированным Apple/ARM.
единственное разумное решение я могу придумать, что вы можете создать приложение генератора, которое вы запускаете на своей машине iOS. Запустите приложение, создайте ключи, а затем скопируйте файл SQLite с устройства. Я бы предположил, что такой файл будет работать на всех устройствах, так как все процессоры ARM, используемые iOS, 32-битные.
опять же, этот ответ немного догадаться. Я собираюсь повторно пометить ваш вопрос как SQLite. Надеюсь, гуру может найти это и сможет взвесить этот вопрос. Мне бы очень хотелось ... знай правду для моей же пользы.