您的位置:首页 > 移动开发 > Objective-C

工作二总结——objective-C中sqlite3数据库的处理(其二)

2014-11-02 18:40 197 查看
这篇是其一中工作的最终实现,使用了FMDB,更具有objective-C的风格。

FMDatabase *db = [[FMDatabase alloc] initWithPath:[NSString stringWithFormat:@"%s", databasePath]];
if ([db open] == NO) {
NSLog(@"Failed to open database!");
return 1;
}
首先打开路径下的数据库,顺便有个小技巧,如何把C的字符串转换为NSString?我就是用stringWithFormat:@"%s"的方式。

[db open]进行的操作是打开数据库,并返回BOOL值表示是否打开成功。如果没有数据库,会自行创建。

依然如同(其一),先删去数据库相应的表再添加新表:

// 为了防止表格式有问题,因此先删去同名表,再创建相应表
[db executeUpdateWithFormat:@"drop table entity;"];
BOOL ret = [db executeUpdateWithFormat:@"create table entity(id integer not null, name TEXT not null unique, score integer, popularity integer);"];
if (ret != YES) {
NSLog(@"Creating table entity failed!");
exit(1);
}
数据库对象的实例方法executeUpdate和executeUpdateWithFormat用于更改数据库内容。DROP、CREATE、INSERT、DELETE都在其列。

两者的区别是对格式占位符不同,WithFormat会用%@、%s之类的占位符,而前者全部占位符都是?。

本文还是那三个表,entity表和tag表的name字段必须唯一。entity非常好说,一行就一个,不会有重复的情况,而tag呢?

上一篇中完全没有进行判断,直接依靠数据库的unique字段来搞定。

然而实际工作中出现的情况是,往往有上万个entity,而tag只有十几个。为了这十几个不断重复的tag我们要进行entity数量*4次数据库插入,要知道数据库的数据交换比内存中直接交换要慢的多,这十万次数据库处理的时间开销实在不忍直视。

因此我用内存中处理的方式判断tag是否重复,也就是一个NSMutableDictionary。

NSMutableDictionary *allTags = [NSMutableDictionary dictionary];/
key当然是tag名称,而value则是tag的id,如此,如果出现

allTags[tagObject] == nil;
的情况,就可以判断没有这个tag,可以加入到数据库中。

如果不是这样,那么肯定可以得到tag的id,(其一)使用select语句来查找tag的id,而在这里,NSMutableDictionary就能解决这个问题,又减少了一次数据库查询开销,真是一举两得!

dictionary的实质是个hash table,其查询效率还是很高的。

插入数据的话,使用

NSString *SQL = [NSString stringWithFormat:@"insert into entity(id,name,score,popularity) values(%d,'%s',%s, %d);", entityCount, entityName, entityScore, 0];
ret = [db executeUpdate:SQL];
我不知道为什么,直接用FMDB的Format老出现问题,因此我直接调整好格式输入就行。

直接贴全文好了,我注释做的很认真,应该可以看懂。

本文没有用到select语句,其实FMDBselect语句极其简单,它会将查询结果储存在一个resultSet里面,之后根据resultSet的方法获取需要的数据即可。

我会在(其三)中讲解select语句用法。

下篇文章就不是这个电脑上的工作了,下个工作是把生成的数据库的这三个表加载到iPhone上,制成iphone上的4个NSDictionary。

第一个是tag对应其映射的entity列表

第二个是entity对应其映射的tag列表

第三个是tag对应其分数

第四个是entity对应其分数

根据最初乱码文本得到的这4个NSDictionary在之后的工作中会有很大作用。

#import <Foundation/Foundation.h>
#import "FMDB.h"

const char *databasePath;
const char *txtPath;

int entityCount, tagCount, mapCount;

int main(int argc, const char * argv[]) {
if (argc != 3) {
NSLog(@"Wrong arg number! Please type in txt path at first, and then the db path!");
return 1;
}
txtPath = argv[1];
FILE *txtFile = fopen(txtPath, "r");
if (!txtFile) {
NSLog(@"File loading is failed!");
return 1;
}
databasePath = argv[2];
FMDatabase *db = [[FMDatabase alloc] initWithPath:[NSString stringWithFormat:@"%s", databasePath]];
if ([db open] == NO) {
NSLog(@"Failed to open database!");
return 1;
}

// 为了防止表格式有问题,因此先删去同名表,再创建相应表
[db executeUpdateWithFormat:@"drop table entity;"];
BOOL ret = [db executeUpdateWithFormat:@"create table entity(id integer not null, name TEXT not null unique, score integer, popularity integer);"];
if (ret != YES) {
NSLog(@"Creating table entity failed!");
exit(1);
}
[db executeUpdateWithFormat:@"drop table tag;"];
ret = [db executeUpdateWithFormat:@"create table tag(id integer not null, name TEXT not null unique, score integer, popularity integer);"];
if (ret != YES) {
NSLog(@"Creating table tag failed!");
exit(1);
}
[db executeUpdateWithFormat:@"drop table map;"];
ret = [db executeUpdateWithFormat:@"create table map(id integer not null, entity_id not null, tag_id integer);"];
if (ret != YES) {
NSLog(@"Creating table map failed!");
exit(1);
}

// 用C的方式操作文本文件更加方便
char stringFromTxt[500], last[500]; //开始读取txt中数据,stringFromTxt代表txt一行,last表示上次处理的一行
fgets(stringFromTxt, 500, txtFile);
memset(last, 0, sizeof(last));
NSMutableDictionary *allTags = [NSMutableDictionary dictionary];//内存判断tag是否重复比让数据库判断unique值更快
while (1) {
// 每趟循环处理文本的一行,循环结束条件为“到达最后一行”,经测试,下面两种情况覆盖了结束条件
if (strcmp(last, stringFromTxt) == 0 || stringFromTxt == NULL){
break;
}
// 插入entity和score的数据
char *entityName = strtok(stringFromTxt," \t");
char *entityScore = strtok(NULL, " \t");
NSString *SQL = [NSString stringWithFormat:@"insert into entity(id,name,score,popularity) values(%d,'%s',%s, %d);", entityCount, entityName, entityScore, 0];
ret = [db executeUpdate:SQL];
if (ret != YES) NSLog(@"插入entity失败,第%d次", entityCount);

// 开始处理tag,txt文件中出现过一个entity对应多个相同tag的情况,在map表构造中应该极力避免这种情况,用mutableSet可以保证高效
NSMutableSet *tagSet = [NSMutableSet set];
char *tag = strtok(NULL, " \n\t");
while (tag != NULL) {
// entity对应了两个相同的tag,直接跳过,处理下一个tag
NSString *tagObject = [NSString stringWithFormat:@"%s", tag];//会多次使用,因此先存下来
if ([tagSet containsObject:tagObject] == NO) {
[tagSet addObject:tagObject];
} else {
tag = strtok(NULL, " \t\n");
continue;
}
// 如果是新出现的tag,就放入tag总集,查询开销为O(log(tag数))
NSNumber *tagId = allTags[tagObject];
if (tagId == nil) {
tagId = [NSNumber numberWithInt:tagCount];
[allTags setObject:tagId forKey:tagObject];

// 随机得到tagScore,并插入tag表,只有新出现的tag才有必要加入tag表
int tagScore = arc4random() % 101;
SQL = [NSString stringWithFormat:@"insert into tag(id,name,score,popularity) values(%d, '%s', %d, %d);", tagCount ++, tag, tagScore, 0];
ret = [db executeUpdate:SQL];
}
// 需要将所有tag映射关系添加至map表
int intTagId = [tagId intValue];
SQL = [NSString stringWithFormat:@"insert into map(id,entity_id,tag_id) values(%d, %d, %d);", mapCount++, entityCount, intTagId];
[db executeUpdate: SQL];

tag = strtok(NULL, " \t\n");
}

strcpy(last, stringFromTxt);
fgets(stringFromTxt, 500, txtFile);
entityCount ++;
}
[db close];
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: