i os sqlite

一,sqlite 简介

前面写了一篇博文讲如何在 C# 中使用 ADO 访问各种数据库,在移动开发和嵌入式领域也有一个轻量级的开源关系型数据库-sqlite。它的特点是零配置(无需服务器),单磁盘文件存储数据(就像fopen一样),平台无关性,使用起来简单又高效。这些特点让其非常适合移动开发和嵌入式领域。当然,sqlite 也因其力求简单高效,也就限制了它对并发,海量数据的处理。下面,我就再接再厉,来讲讲如何在 iOS 中使用 sqlite 库和第三方封装库 FMDB,以及介绍一个 MAC 下开源的可视化 sqlite 浏览器。

本文源码:https://github.com/kesalin/iOSSnippet/tree/master/SQLiteDemo

二,在 iOS 中的使用

在 iOS 中 sqlite3 库是一套纯 C 的接口,因此很方便地就可以在 obj-c 源码中无痕使用它,而且其使用方式与用 ADO 方式操作数据库大同小异-少了创建数据库链接一环而已(因为 sqlite 没有服务器的概念也就无需链接了)。

  • 首先,需要引入 libsqlite3.0.dylib:

然后包含头文件:

#import "/usr/include/sqlite3.h"

  • 打开或创建数据库
SQLITE_API int sqlite3_open(
  const char *filename,   /* Database filename (UTF-8) */
  sqlite3 **ppDb          /* OUT: SQLite db handle */
);

使用示例:(dbPath 为 NSString *)

复制代码
    // open database
    //
    int state = sqlite3_open([dbPath UTF8String], &database);
    if (state == SQLITE_OK) {
        DLOG(@" >> Succeed to open database. %@", dbPath);
    }
    else {
        DLOG(@" >> Failed to open database. %@", dbPath);
    }
复制代码
  • 关闭数据库
SQLITE_API int sqlite3_close(sqlite3 *);

上面这个接口将关闭数据库,如果当前还有事务没有提交,会先执行 rollback 操作,然后再关闭数据库。

  • 执行 sql 语句
复制代码
SQLITE_API int sqlite3_exec(
  sqlite3*,                                  /* An open database */
  const char *sql,                           /* SQL to be evaluated */
  int (*callback)(void*,int,char**,char**),  /* Callback function */
  void *,                                    /* 1st argument to callback */
  char **errmsg                              /* Error msg written here */
);
复制代码

这个接口是最常用到的,几乎除了查询之外的 sql 命令都可以用它来操作,比如创建表,插入/更新/删除记录,创建/提交/回滚事务等。注意:如果 errmsg 不为 null,那么当错误发生时, sqlite 就会为错误消息分配内存,返回给调用者,调用者有责任调用 sqlite3_free 来释放这部分内存。为了方便使用,我封装了一个简单的 obj-c 方法:

复制代码
- (BOOL)excuteSQLWithCString:(const char *)sqlCmd
{
    char * errorMsg;
    int state = sqlite3_exec(database, sqlCmd, NULL, NULL, &errorMsg);
    if (state == SQLITE_OK) {
        DLOG(@" >> Succeed to %@",
             [NSString stringWithCString:sqlCmd encoding:NSUTF8StringEncoding]);
    }
    else {
        DLOG(@" >> Failed to %@. Error: %@",
             [NSString stringWithCString:sqlCmd encoding:NSUTF8StringEncoding],
             [NSString stringWithCString:errorMsg encoding:NSUTF8StringEncoding]);
        
        sqlite3_free(errorMsg);
    }
    
    return (state == SQLITE_OK);
}
复制代码

下面是创建表以及事务操作的使用示例:

复制代码
- (void)createTable
{
    if (database == NULL) {
        DLOG(@" >> Database does not open yet.");
        return;
    }
    
    const char * sqlCmd = "create table if not exists customer (id integer primary key autoincrement, name text not null, address text, age integer)";
    
    [self excuteSQLWithCString:sqlCmd];
}

- (BOOL)beginTransaction
{
    return [self excuteSQLWithCString:"BEGIN EXCLUSIVE TRANSACTION;"];
}

- (BOOL)commit
{
    return [self excuteSQLWithCString:"COMMIT TRANSACTION;"];    
}

- (BOOL)rollback
{
    return [self excuteSQLWithCString:"ROLLBACK TRANSACTION;"];
}
复制代码

很简单,不是么?至于插入,更新,删除示例,请参考如下 sqlCmd:

复制代码
// insert
    NSString * sqlCmd = [NSString stringWithFormat:@"insert into customer (name, address, age) values ('%@', '%@', %d)",
                           customer.name, customer.address, customer.age];

// update
    NSString * sqlCmd = [NSString stringWithFormat:@"update customer set address='%@',age=%d where name='%@'",
                         newValue.address, newValue.age, oldValue.name];

// delete
    NSString * sqlCmd = [NSString stringWithFormat:@"delete from customer where name='%@'",
                           customer.name];
复制代码
  • 查询操作

查询操作稍微负责一点,需要创建查询描述(sqlite3_stmt),然后调用如下接口编译成字节程序:

复制代码
SQLITE_API int sqlite3_prepare_v2(
  sqlite3 *db,            /* Database handle */
  const char *zSql,       /* SQL statement, UTF-8 encoded */
  int nByte,              /* Maximum length of zSql in bytes. */
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
);
复制代码

注意:这里使用的是 v2 - version 2,这是 sqlite 推荐使用的版本,version 1 仅仅是为了向后兼容而保留着。

然后使用如下接口来评估的查询结果:

SQLITE_API int sqlite3_step(sqlite3_stmt*);

如果该接口返回 SQLITE_ROW,表面查询到了一行记录,我们就可以以下接口从查询描述中获取我们想要的值:

复制代码
SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol);
SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol);
SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol);
SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
复制代码

最后,需要通过如下接口释放先前创建的查询描述。通常,为了提高查询效率,可以把常用的查询描述缓存起来。

SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);

下面就来看一个具体的使用示例:

复制代码
- (NSArray *)queryAllCustomers
{
    NSMutableArray * array = [[NSMutableArray alloc] init];
    
    const char * sqlCmd = "select name, address, age from customer";
    sqlite3_stmt * statement;
    int state = sqlite3_prepare_v2(database, sqlCmd, -1, &statement, nil);
    if (state == SQLITE_OK) {
        DLOG(@" >> Succeed to prepare statement. %@",
             [NSString stringWithCString:sqlCmd encoding:NSUTF8StringEncoding]);
    }
    
    NSInteger index = 0;
    while (sqlite3_step(statement) == SQLITE_ROW) {
        // get raw data from statement
        //
        char * cstrName = (char *)sqlite3_column_text(statement, 0);
        char * cstrAddress = (char *)sqlite3_column_text(statement, 1);
        int age = sqlite3_column_int(statement, 2);
        
        NSString * name = [NSString stringWithCString:cstrName encoding:NSUTF8StringEncoding];
        NSString * address = [NSString stringWithCString:cstrAddress encoding:NSUTF8StringEncoding];
        KSCustomer * customer = [[KSCustomer alloc]
                                 initWith:name
                                 address:address
                                 age:age];
        [array addObject:customer];
        
        DLOG(@"   >> Record %d : %@ %@ %d", index++, name, address, age);
    }
    
    sqlite3_finalize(statement);
    
    DLOG(@" >> Query %d records.", [array count]);
    return array;
}
复制代码

三,MAC 下查看 sqlite db 文件的工具

MAC 下有一款不错的开源可视化 sqlite db 浏览器:SQLite Database Browser,你可以从以下链接获取:

http://sourceforge.net/projects/sqlitebrowser/

该软件运行界面如下:

四,封装 sqlite 的第三方库 FMDB

在 iOS 中直接使用 sqlite 原生 C 接口还是不那么方便,因此催生了第三方的 iOS 版封装库,其中使用比较广泛又轻量级的就是 FMDB(https://github.com/ccgus/fmdb),目前该库只有六个文件,不超过2000行代码。

使用也是非常简单,在工程中包含这六个文件:

然后包含头文件:

#import "FMDatabase.h"
#import "FMResultSet.h"
#import "FMDatabaseAdditions.h"

就可以使用该库了:

复制代码
    // Create database
    //
    NSString * path  = [UIHUtilities configPathFor:kDatabaseFile];
    FMDatabase db  = [[FMDatabase databaseWithPath:path] retain];
    
    if (![db open])
    {  
        DLog(@" >> Error: Failed to open database at %@", path);
    }
    
#if DEBUG
    db.traceExecution = TRUE;
#endif
    
    // Create tables
    //
    [db executeUpdate:@"CREATE TABLE Image (studyUid text, patientId text, seriesUid text, SOPUid text, contentDate text, modality text, patientPosition text, filepath text, thumbnailPath text)"];

    // insert
    //
    BOOL retValue = [db executeUpdate:@"INSERT INTO Image (studyUid, patientId, seriesUid, SOPUid, contentDate, patientPosition, modality, filepath, thumbnailPath) VALUES (?,?,?,?,?,?,?,?,?)", image.studyUid, image.patientId, image.seriesUid, image.SOPUid, image.contentDate, image.patientPosition, image.modality, image.filepath, image.thumbnailPath];
    
    if (!retValue)
        DLog(@" >> Error: Database failed to insert image %@", image);

    // query
    //
    FMResultSet *rs = [db executeQuery:@"SELECT * FROM Image WHERE SOPUid = ?", SOPUid];
    if ([rs next])
    {
         ....
    }

    // query count
    //
    NSInteger count = 0;
    FMResultSet *rs = [db executeQuery:@"SELECT COUNT(*) FROM Image WHERE seriesUid = ?", seriesUid];
    if ([rs next]) {
        count = [rs intForColumnIndex:0];
    }

    // delete
    //
    retValue = [db executeUpdate:@"DELETE FROM Image WHERE seriesUid = ?", seriesUid];
    if (!retValue)
        DLog(@" >> Error: Database failed to delete image by seriesUid %@", seriesUid);

    // release database
    //
    [db release];
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
def refresh_labels(self): data4 = self.la # 连接到 SQLite 数据库文件,并创建游标对象 cursor() conn = sqlite3.connect(filepath) cursor = conn.cursor() data41 = str(self.la) if not data4.endswith('.xlsx'): data4 += '.xlsx' wo = pinjie filepath = os.path.join(wo, data4) if not os.path.exists(filepath): wb = openpyxl.Workbook() wb.save(filepath) else: wb = openpyxl.load_workbook(filepath) for i, sheet_name in enumerate(self.sheet_names): label = tk.Label(self.unique_listbox, text=sheet_name) label.grid(row=i // 3, column=i % 3, sticky="ew", padx=1, pady=1) current_time = datetime.datetime.now().time() start_time_1 = datetime.time(8, 0, 0) # 早上8点 end_time_1 = datetime.time(20, 0, 0) # 下午7点 start_time_2 = datetime.time(20, 0, 0) # 晚上8点 end_time_2 = datetime.time(7, 0, 0) # 早上7点 for i, sheet_name in enumerate(self.sheet_names): filtered_rows = [] # 优化第二段代码:检查文件是否存在 filepath = os.path.join(pinjie, self.la + '.xlsx') if os.path.exists(filepath): workbook = xl.load_workbook(filepath) sheet = workbook.active today = datetime.datetime.now().strftime('%Y/%m/%d') cell_value = sheet.cell(row=1, column=1).value if cell_value is not None and cell_value != '': for row in sheet.iter_rows(min_row=1): if row[2].value == today and row[8].value == sheet_name: datetime_obj = datetime.datetime.strptime(row[3].value, '%H:%M:%S') row_time = datetime_obj.time() if start_time_1 <= row_time <= end_time_1 and start_time_1 <= current_time <= end_time_1: filtered_rows.append(row) elif start_time_2 <= row_time or current_time <= end_time_2: filtered_rows.append(row) label = self.unique_listbox.grid_slaves(row=i // 3, column=i % 3)[0] if filtered_rows: label.config(text=f"{sheet_name} - 已點檢", fg="green") else: label.config(text=f"{sheet_name} - 未點檢", fg="red")將這段代碼重拼接的excel修改為sqlite3,然後將在這個excel所作的操作,修改為到sqlite3中,其他判斷條件不變,和顯示需求不變
07-14
根据您提供的代码,我已经将其修改为使用SQLite3数据库进行操作,并保持其他判断条件和显示需求不变。请查看下面的代码: ```python import sqlite3 import datetime # 连接到 SQLite 数据库文件,并创建游标对象 cursor() conn = sqlite3.connect('your_database.db') cursor = conn.cursor() # 获取当前时间 current_time = datetime.datetime.now().time() start_time_1 = datetime.time(8, 0, 0) # 早上8点 end_time_1 = datetime.time(20, 0, 0) # 下午7点 start_time_2 = datetime.time(20, 0, 0) # 晚上8点 end_time_2 = datetime.time(7, 0, 0) # 早上7点 for i, sheet_name in enumerate(self.sheet_names): filtered_rows = [] # 构建查询语句 query = f"SELECT * FROM your_table WHERE 列名 = '{sheet_name}'" # 执行查询 cursor.execute(query) # 获取查询结果 rows = cursor.fetchall() # 判断是否有符合条件的行 for row in rows: datetime_obj = datetime.datetime.strptime(row[3], '%H:%M:%S') row_time = datetime_obj.time() if (start_time_1 <= row_time <= end_time_1 and start_time_1 <= current_time <= end_time_1) or (start_time_2 <= row_time or current_time <= end_time_2): filtered_rows.append(row) label = self.unique_listbox.grid_slaves(row=i // 3, column=i % 3)[0] if filtered_rows: label.config(text=f"{sheet_name} - 已點檢", fg="green") else: label.config(text=f"{sheet_name} - 未點檢", fg="red") # 关闭数据库连接 conn.close() ``` 请注意,您需要将代码中的`'your_database.db'`替换为您的SQLite3数据库文件路径,`'your_table'`替换为要查询的表格名称,`'列名'`替换为实际的列名。 这段代码首先连接到SQLite3数据库,并根据工作表名称构建查询语句。然后执行查询并获取结果,根据时间条件筛选出符合要求的行。 希望这次的回答满足了您的要求。如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值