包含组件 最底层File System -> SQLite -> NSPersistent Store(可有多个) -> NSPersistent StoreCoordinator -> NSManagedObjectContext(可有多个,每个可包含多个NSManagedObject)
设置堆栈 范例:https://github.com/objcio/issue-4-full-core-data-application
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 - (void )setupManagedObjectContext { self .managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType ]; self .managedObjectContext.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self .managedObjectModel]; NSError * error; [self .managedObjectContext.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:self .storeURL options:nil error:&error]; if (error) { NSLog (@"error: %@" , error); } self .managedObjectContext.undoManager = [[NSUndoManager alloc] init]; }
创建模型 在xcode新建的Core Data选项中选择Data Model template,模型文件会被编译成.momd文件。模型创建完毕就可以创建与之对应的NSManagedObject子类。从菜单选择Editor > NSManagedObject subclass。
模型的属性
默认/可选:建议不使用带默认值的可选属性
Transient:方便撤销操作和故障处理,建议使用transient属性
索引:提高读取速度
标量类型:默认NSNumber,也可以使用int64_t,float_t或BOOL。
创建Store类 存储类除了managed object context还有rootItem方法,程序启动时会查找这root item然后传给root view controller。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 - (Item*)rootItem { NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Item" ]; request.predicate = [NSPredicate predicateWithFormat:@"parent = %@" , nil ]; NSArray * objects = [self .managedObjectContext executeFetchRequest:request error:NULL ]; Item* rootItem = [objects lastObject]; if (rootItem == nil ) { rootItem = [Item insertItemWithTitle:nil parent:nil inManagedObjectContext:self .managedObjectContext]; } return rootItem; } + (instancetype )insertItemWithTitle:(NSString *)title parent:(Item*)parent inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext { NSUInteger order = parent.numberOfChildren; Item* item = [NSEntityDescription insertNewObjectForEntityForName:self .entityName inManagedObjectContext:managedObjectContext]; item.title = title; item.parent = parent; item.order = @(order); return item; } - (NSUInteger )numberOfChildren { return self .children.count; } - (NSFetchedResultsController *)childrenFetchedResultsController { NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:[self .class entityName]]; request.predicate = [NSPredicate predicateWithFormat:@"parent = %@" , self ]; request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"order" ascending:YES ]]; return [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:self .managedObjectContext sectionNameKeyPath:nil cacheName:nil ]; }
和Table View无缝结合 创建一个NSFetchedResultsController作为table view的data source 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 - (id )initWithTableView:(UITableView *)tableView { self = [super init]; if (self ) { self .tableView = tableView; self .tableView.dataSource = self ; } return self ; } - (void )setFetchedResultsController:(NSFetchedResultsController *)fetchedResultsController { _fetchedResultsController = fetchedResultsController; fetchedResultsController.delegate = self ; [fetchedResultsController performFetch:NULL ]; } - (NSInteger )numberOfSectionsInTableView:(UITableView *)tableView { return self .fetchedResultsController.sections.count; } - (NSInteger )tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger )sectionIndex { id <NSFetchedResultsSectionInfo > section = self .fetchedResultsController.sections[sectionIndex]; return section.numberOfObjects; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { id object = [self .fetchedResultsController objectAtIndexPath:indexPath]; id cell = [tableView dequeueReusableCellWithIdentifier:self .reuseIdentifier forIndexPath:indexPath]; [self .delegate configureCell:cell withObject:object]; return cell; }
创建Table View Controller 在新建的Table view的viewDidLoad里写:
1 2 3 4 5 6 7 8 fetchedResultsControllerDataSource = [[FetchedResultsControllerDataSource alloc] initWithTableView:self .tableView]; self .fetchedResultsControllerDataSource.fetchedResultsController = self .parent.childrenFetchedResultsController;fetchedResultsControllerDataSource.delegate = self ; fetchedResultsControllerDataSource.reuseIdentifier = @"Cell" ;
实现delegate
1 2 3 4 5 6 7 8 9 10 11 12 - (void )configureCell:(id )theCell withObject:(id )object { UITableViewCell * cell = theCell; Item* item = object; cell.textLabel.text = item.title; }
添加在textFieldShouldReturn:里 1 2 3 4 5 6 7 8 9 10 [Item insertItemWithTitle:title parent:self .parent inManagedObjectContext:self .parent.managedObjectContext]; textField.text = @"" ; [textField resignFirstResponder];
增删改后table view也会更改显示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 - (void )controller:(NSFetchedResultsController *)controller didChangeObject:(id )anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType )type newIndexPath:(NSIndexPath *)newIndexPath { if (type == NSFetchedResultsChangeInsert ) { [self .tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic ]; } } - (void )controllerWillChangeContent:(NSFetchedResultsController *)controller { [self .tableView beginUpdates]; } - (void )controllerDidChangeContent:(NSFetchedResultsController *)controller { [self .tableView endUpdates]; }
和Collection View的结合 范例:https://github.com/AshFurrow/UICollectionView-NSFetchedResultsController collection view没有beginUpdates和endUpdates方法,所以只能用performBatchUpdate方法收集所有更新,然后在controllerDidChangeContent中用block执行所有更新。
如何传递Table view里的Model对象到新的view controller中 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 - (void )prepareForSegue:(UIStoryboardSegue *)segue sender:(id )sender { [super prepareForSegue:segue sender:sender]; if ([segue.identifier isEqualToString:selectItemSegue]) { [self presentSubItemViewController:segue.destinationViewController]; } } - (void )presentSubItemViewController:(ItemViewController*)subItemViewController { Item* item = [self .fetchedResultsControllerDataSource selectedItem]; subItemViewController.parent = item; } - (void )viewWillAppear:(BOOL )animated { [super viewWillAppear:animated]; self .fetchedResultsControllerDataSource.paused = NO ; } - (void )viewWillDisappear:(BOOL )animated { [super viewWillDisappear:animated]; self .fetchedResultsControllerDataSource.paused = YES ; } - (void )setPaused:(BOOL )paused { _paused = paused; if (paused) { self .fetchedResultsController.delegate = nil ; } else { self .fetchedResultsController.delegate = self ; [self .fetchedResultsController performFetch:NULL ]; [self .tableView reloadData]; } }
删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 - (BOOL )tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { return YES ; } - (void )tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle )editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete ) { id object = [self .fetchedResultsController objectAtIndexPath:indexPath]; [self .delegate deleteObject:object]; } } - (void )prepareForDeletion { NSSet * siblings = self .parent.children; NSPredicate * predicate = [NSPredicate predicateWithFormat:@"order > %@" , self .order]; NSSet * siblingsAfterSelf = [siblings filteredSetUsingPredicate:predicate]; [siblingsAfterSelf enumerateObjectsUsingBlock:^(Item* sibling, BOOL * stop) { sibling.order = @(sibling.order.integerValue - 1 ); }]; }
增加删除的动画效果
1 2 3 4 5 6 7 8 9 else if (type == NSFetchedResultsChangeDelete ) { [self .tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic ]; }
增加晃动撤销功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 application.applicationSupportsShakeToEdit = YES ; - (BOOL )canBecomeFirstResponder { return YES ; } - (NSUndoManager *)undoManager { return self .managedObjectContext.undoManager; } self .managedObjectContext.undoManager = [[NSUndoManager alloc] init];NSString * title = textField.text;NSString * actionName = [NSString stringWithFormat:NSLocalizedString (@"add item \"%@\"" , @"Undo action name of add item" ), title];[self .undoManager setActionName:actionName]; [self .store addItem:title parent:nil ];
排序 可以参考官方文档:https://developer.apple.com/library/ios/documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40008228-CH1-SW14
保存
Fetch获取对象 基础 官方文档:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdFetching.html
范例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 request.result = [NSPredicate predicateWithFormat: @"(%@ <= longitude) AND (longitude <= %@)" @"AND (%@ <= latitude) AND (latitude <= %@)" , @(minLongitude), @(maxLongitude), @(minLatitude), @(maxLatitude)]; request.returnsObjectsAsFaults = NO ; request.fetchLimit = 200 ; NSError *error = nil ;NSArray *stops = [moc executeFetchRequest:request error:&error];NSAssert (stops != nil , @"Failed to execute %@: %@" , request, error);NSPredicate *exactPredicate = [self exactLatitudeAndLongitudePredicateForCoordinate:self .location.coordinate];stops = [stops filteredArrayUsingPredicate:exactPredicate]; - (NSPredicate *)exactLatitudeAndLongitudePredicateForCoordinate:(CLLocationCoordinate2D )pointOfInterest; { return [NSPredicate predicateWithBlock:^BOOL (Stop *evaluatedStop, NSDictionary *bindings) { CLLocation *evaluatedLocation = [[CLLocation alloc] initWithLatitude:evaluatedStop.latitude longitude:evaluatedStop.longitude]; CLLocationDistance distance = [self .location distanceFromLocation:evaluatedLocation]; return (distance < self .distance); }]; } NSPredicate *timePredicate = [NSPredicate predicateWithFormat:@"(%@ <= departureTime) && (departureTime <= %@)”, startDate, endDate]; NSPredicate *predicate = [NSPredicate predicateWithFormat: @" (SUBQUERY(stopTimes, $x, (%@ <= $x.departureTime) && ($x.departureTime <= %@)).@count != 0 )”, startDate, endDate];NSString *searchString = @"U Görli" ;predicate = [NSPredicate predicateWithFormat:@"name BEGINSWITH %@" , searchString];
导入大量数据 应用Bundle里的SQLite文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 NSFileManager * fileManager = [NSFileManager defaultManager];NSError *error;if ([fileManager fileExistsAtPath:self .storeURL.path]) { NSURL *storeDirectory = [self .storeURL URLByDeletingLastPathComponent]; NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:storeDirectory includingPropertiesForKeys:nil options:0 errorHandler:NULL ]; NSString *storeName = [self .storeURL.lastPathComponent stringByDeletingPathExtension]; for (NSURL *url in enumerator) { if (![url.lastPathComponent hasPrefix:storeName]) continue ; [fileManager removeItemAtURL:url error:&error]; } } NSString * bundleDbPath = [[NSBundle mainBundle] pathForResource:@"seed" ofType:@"sqlite" ];[fileManager copyItemAtPath:bundleDbPath toPath:self .storeURL.path error:&error]; NSString * bundleVersion = [infoDictionary objectForKey:(NSString *)kCFBundleVersionKey];NSString *seedVersion = [[NSUserDefaults standardUserDefaults] objectForKey@"SeedVersion" ];if (![seedVersion isEqualToString:bundleVersion]) { } NSDictionary *infoDictionary = [NSBundle mainBundle].infoDictionary;[[NSUserDefaults standardUserDefaults] setObject:bundleVersion forKey:@"SeedVersion" ];
导入范例 https://github.com/objcio/issue-4-importing-and-fetching
版本迁移 Mapping Models NSMigrationManager能够推断两个版本模型的映射关系,但是如果版本跨度大了就力不从心了。
Progressive Migrations渐进式迁移 实现原理是两个版本之间确保正常,升级时按照一个版本一个版本渐进式的升级方式,比如最新的版本是第四版,如果用户使用的是第二版的,那么升级是就是先从第二版升级到第三版,然后再从第三版升级到第四版。完整范例:https://github.com/objcio/issue-4-core-data-migration 主要代码来自Marcus Zarrahttps://twitter.com/mzarra ,他的书关于Core Data的值得一看,http://pragprog.com/book/mzcd2/core-data
迁移策略 NSEntityMigrationPolicy这个类不光能够修改Entity的属性和关系,还能够自定义一些操作完成每个Entity的迁移。例如在Entity Mapping的Custom Polity里写上自定义的polity的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 NSNumber *modelVersion = [mapping.userInfo valueForKey:@"modelVersion" ];if (modelVersion.integerValue == 2 ) { NSMutableArray *sourceKeys = [sourceInstance.entity.attributesByName.allKeys mutableCopy]; NSDictionary *sourceValues = [sourceInstance dictionaryWithValuesForKeys:sourceKeys]; NSManagedObject *destinationInstance = [NSEntityDescription insertNewObjectForEntityForName:mapping.destinationEntityName inManagedObjectContext:manager.destinationContext]; NSArray *destinationKeys = destinationInstance.entity.attributesByName.allKeys; for (NSString *key in destinationKeys) { id value = [sourceValues valueForKey:key]; if (value && ![value isEqual:[NSNull null]]) { [destinationInstance setValue:value forKey:key]; } } } NSMutableDictionary *authorLookup = [manager lookupWithKey:@"authors" ];NSString *authorName = [sourceInstance valueForKey:@"author" ];NSManagedObject *author = [authorLookup valueForKey:authorName];if (!author) { [authorLookup setValue:author forKey:authorName]; } [destinationInstance performSelector:@selector (addAuthorsObject:) withObject:author]; [manager associateSourceInstance:sourceInstance withDestinationInstance:destinationInstance forEntityMapping:mapping]; return YES ;
NSMigrationManager的category方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @implementation NSMigrationManager (Lookup )- (NSMutableDictionary *)lookupWithKey:(NSString *)lookupKey { NSMutableDictionary *userInfo = (NSMutableDictionary *)self .userInfo; if (!userInfo) { userInfo = [@{} mutableCopy]; self .userInfo = userInfo; } NSMutableDictionary *lookup = [userInfo valueForKey:lookupKey]; if (!lookup) { lookup = [@{} mutableCopy]; [userInfo setValue:lookup forKey:lookupKey]; } return lookup; } @end
更复杂的迁移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 NSArray *users = [sourceInstance valueForKey:@"users" ];for (NSManagedObject *user in users) { NSManagedObject *file = [NSEntityDescription insertNewObjectForEntityForName:@"File" inManagedObjectContext:manager.destinationContext]; [file setValue:[sourceInstance valueForKey:@"fileURL" ] forKey:@"fileURL" ]; [file setValue:destinationInstance forKey:@"book" ]; NSInteger userId = [[user valueForKey:@"userId" ] integerValue]; NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"User" ]; request.predicate = [NSPredicate predicateWithFormat:@"userId = %d" , userId]; NSManagedObject *user = [[manager.destinationContext executeFetchRequest:request error:nil ] lastObject]; [file setValue:user forKey:@"user" ]; }
数据量大时的迁移改造,利用CoreData提供的chunks数据块方式。官方文档https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/CoreDataVersioning/Articles/vmCustomizing.html#//apple_ref/doc/uid/TP40004399-CH8-SW9
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 NSArray *mappingModels = @[mappingModel]; if ([self .delegate respondsToSelector:@selector (migrationManager:mappingModelsForSourceModel:)]) { NSArray *explicitMappingModels = [self .delegate migrationManager:self mappingModelsForSourceModel:sourceModel]; if (0 < explicitMappingModels.count) { mappingModels = explicitMappingModels; } } for (NSMappingModel *mappingModel in mappingModels) { didMigrate = [manager migrateStoreFromURL:sourceStoreURL type:type options:nil withMappingModel:mappingModel toDestinationURL:destinationStoreURL destinationType:type destinationOptions:nil error:error]; } - (NSArray *)migrationManager:(MHWMigrationManager *)migrationManager mappingModelsForSourceModel:(NSManagedObjectModel *)sourceModel { NSMutableArray *mappingModels = [@[] mutableCopy]; NSString *modelName = [sourceModel mhw_modelName]; if ([modelName isEqual:@"Model2" ]) { } return mappingModels; } - (NSString *)mhw_modelName { NSString *modelName = nil ; NSArray *modelPaths = for (NSString *modelPath in modelPaths) { NSURL *modelURL = [NSURL fileURLWithPath:modelPath]; NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; if ([model isEqual:self ]) { modelName = modelURL.lastPathComponent.stringByDeletingPathExtension; break ; } } return modelName; }
建立单元测试,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 - (void )setUpCoreDataStackMigratingFromStoreWithName:(NSString *)name { NSURL *storeURL = [self temporaryRandomURL]; [self copyStoreWithName:name toURL:storeURL]; NSURL *momURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd" ]; self .managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:momURL]; NSString *storeType = NSSQLiteStoreType ; MHWMigrationManager *migrationManager = [MHWMigrationManager new]; [migrationManager progressivelyMigrateURL:storeURL ofType:storeType toModel:self .managedObjectModel error:nil ]; self .persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self .managedObjectModel]; [self .persistentStoreCoordinator addPersistentStoreWithType:storeType configuration:nil URL:storeURL options:nil error:nil ]; self .managedObjectContext = [[NSManagedObjectContext alloc] init]; self .managedObjectContext.persistentStoreCoordinator = self .persistentStoreCoordinator; } - (NSURL *)temporaryRandomURL { NSString *uniqueName = [NSProcessInfo processInfo].globallyUniqueString; return [NSURL fileURLWithPath:[NSTemporaryDirectory () stringByAppendingString:uniqueName]]; } - (void )copyStoreWithName:(NSString *)name toURL:(NSURL *)url { NSBundle *bundle = [NSBundle bundleForClass:[self class ]]; NSFileManager *fileManager = [NSFileManager new]; NSString *path = [bundle pathForResource:[name stringByDeletingPathExtension] ofType:name.pathExtension]; [fileManager copyItemAtPath:path toPath:url.path error:nil ]; } - (void )setUp { [super setUp]; [self setUpCoreDataStackMigratingFromStoreWithName:@"Model1.sqlite" ]; }
调试迁移一个有用的启动参数是-com.apple.CoreData.MigrationDebug,设置1就会在console收到迁移数据时会出现的特殊的情况的信息。如果设置-com.apple.CoreData.SQLDebug 为 1还能够在console看到实际操作的SQL语句。
Core Data的并行处理
性能测试
加上-com.apple.CoreData.SQLDebug1 作为启动参数传递给应用程序可以得到的输出
1 2 3 4 5 6 7 sql: SELECT 0 , t0.Z_PK, t0.Z_OPT, t0.ZIDENTIFIER, t0.ZLATITUDE, t0.ZLONGITUDE, t0.ZNAME FROM ZSTOP t0 WHERE (? <= t0.ZLONGITUDE AND t0.ZLONGITUDE <= ? AND ? <= t0.ZLATITUDE AND t0.ZLATITUDE <= ?) LIMIT 100 annotation: sql connection fetch time: 0.0008 s annotation: total fetch execution time: 0.0013 s for 15 rows.
实际生成的SQL是:
1 2 3 4 5 6 SELECT 0 , t0.Z_PK, t0.Z_OPT, t0.ZIDENTIFIER, t0.ZLATITUDE, t0.ZLONGITUDE, t0.ZNAME FROM ZSTOP t0 WHERE (? <= t0.ZLONGITUDE AND t0.ZLONGITUDE <= ? AND ? <= t0.ZLATITUDE AND t0.ZLATITUDE <= ?) LIMIT 200
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 % cd TrafficSearch % sqlite3 transit-data.sqlite SQLite version 3.7 .13 2012 -07 -17 17 :46 :21 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> EXPLAIN QUERY PLAN SELECT 0 , t0.Z_PK, t0.Z_OPT, t0.ZIDENTIFIER, t0.ZLATITUDE, t0.ZLONGITUDE, t0.ZNAME FROM ZSTOP t0 ...> WHERE (13.30845219672199 <= t0.ZLONGITUDE AND t0.ZLONGITUDE <= 13.33441458422844 AND 52.42769566863058 <= t0.ZLATITUDE AND t0.ZLATITUDE <= 52.44352370653525 ) ...> LIMIT 100 ; 0 |0 |0 |SEARCH TABLE ZSTOP AS t0 USING INDEX ZSTOP_ZLONGITUDE_INDEX (ZLONGITUDE>? AND ZLONGITUDE<?) (~6944 rows)
输出
1 2 0 |0 |0 |SEARCH TABLE ZSTOP AS t0 USING INDEX ZSTOP_ZLONGITUDE_ZLATITUDE (ZLONGITUDE>? AND ZLONGITUDE<?) (~6944 rows)