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

这篇是其一中工作的最终实现,使用了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;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值