首页 > 代码库 > SQLite

SQLite

由于在iPhone3.0上已经支持了Core Data,是苹果一个新的API,并且是基于SQlite的。速度也是非常快吧。所以我们对SQLite仅需要懂一些即可,以下是一些基础信息


一、:常用接口(2个重要结构体和5个主要函数)
sqlite3             *pdb, 数据库句柄,跟文件句柄 FILE 很类似
sqlite3_stmt     *stmt, 这个相当于 ODBC 的 Command 对象,用于保存编译好的 SQL语句


sqlite3_open(),   打开数据库
sqlite3_exec(),   执行非查询的 sql 语句
sqlite3_prepare(), 准备 sql 语句,执行 select 语句或者要使用 parameter bind时,用这个函数(封装了 sqlite3_exec ) .
Sqlite3_step(), 在调用 sqlite3_prepare 后,使用这个函数在记录集中移动。
Sqlite3_close(), 关闭数据库文件
 
还有一系列的函数,用于从记录集字段中获取数据,如
sqlite3_column_text(), 取 text 类型的数据。
sqlite3_column_blob (),取 blob 类型的数据
sqlite3_column_int(), 取 int 类型的数据


其他工具函数
1. 得到结果总共的行数
int sqlite3_data_count(sqlite3_stmt *pStmt);
如果过程没有返回值,如update,将返回0
2. 得到当前行中包含的数据个数
int sqlite3_column_count(sqlite3_stmt *pStmt);
如果sqlite3_step返回SQLITE_ROW,可以得到列数,否则为零。
3. 得到数据行中某个列的数据
sqlite3_column_xxx(sqlite3_stmt*, int iCol);
在sqlite3_step返回SQLITE_ROW后,使用它得到第iCol列的数据。
其中的xxx代表:
blob:指向保存数据内存的指针
bytes, bytes16: 得到该blob类型数据的大小,或者text转换为UTF8/UTF16的字符串长度。
double, int, int64: 数值
text,text16:字符串指针
type:该列的数据类型(SQLITE_INTEGER,SQLITE_FLOAT,SQLITE_TEXT,SQLITE_BLOB,SQLITE_NULL)
注意:如果对该列使用了不同与该列本身类型适合的数据读取方法,得到的数值将是转换过的结果。
4. 得到数据行中某个列的数据的类型
int sqlite3_column_type(sqlite3_stmt*, int iCol);
返回值:SQLITE_INTEGER,SQLITE_FLOAT,SQLITE_TEXT,SQLITE_BLOB,SQLITE_NULL
使用的方法和sqlite3_column_xxx()函数类似。


二、:sqlite数据类型介绍
    在进行数据库Sql操作之前,首先有个问题需要说明,就是Sqlite的数据类型,和其他的数据库不同,Sqlite支持的数据类型有他自己的特色,这个特色有时会被认为是一个潜在的缺点,但是这个问题并不在我们的讨论范围之内。
大多数的数据库在数据类型上都有严格的限制,在建立表的时候,每一列都必须制定一个数据类型,只有符合该数据类型的数据可以被保存在这一列当中。而在Sqlite2.X中,数据类型这个属性只属于数据本生,而不和数据被存在哪一列有关,也就是说数据的类型并不受数据列限制(有一个例外:INTEGERPRIMARY KEY,该列只能存整型数据)。
但是当Sqlite进入到3.0版本的时候,这个问题似乎又有了新的答案,Sqlite的开发者开始限制这种无类型的使用,在3.0版本当中,每一列开始拥有自己的类型,并且在数据存入该列的时候,数据库会试图把数据的类型向该类型转换,然后以转换之后的类型存储。当然,如果转换被认为是不可行的,Sqlite仍然会存储这个数据,就像他的前任版本一样。
举个例子,如果你企图向一个INTEGER类型的列中插入一个字符串,Sqlite会检查这个字符串是否有整型数据的特征,如果有而且可以被数据库所识别,那么该字符串会被转换成整型再保存,如果不行,则还是作为整型存储。


总的来说,所有存在Sqlite 3.0版本当中的数据都拥有以下之一的数据类型:
空(NULL):该值为空
整型(INTEGEER):有符号整数,按大小被存储成1,2,3,4,6或8字节。
实数(REAL):浮点数,以8字节指数形式存储。
文本(TEXT):字符串,以数据库编码方式存储(UTF-8, UTF-16BE 或者 UTF-16-LE)。
BLOB:BLOB数据不做任何转换,以输入形式存储。


ps:在关系数据库中,CLOB和BLOB类型被用来存放大对象。BOLB表示二进制大对象,这种数据类型通过用来保存图片,图象,视频等。CLOB表示字符大对象,能够存放大量基于字符的数据。


对应的,对于数据列,同样有以下的数据类型:
TEXT
NUMERIC
INTEGER
REAL
NONE
数据列的属性的作用是确定对插入的数据的转换方向:
TEXT 将数据向文本进行转换,对应的数据类型为NULL,TEXT 或 BLOB
NUMERIC 将数据向数字进行转换,对应的数据类型可能为所有的五类数据,当试图存入文本时将执行向整型或浮点类型的转换(视具体的数值而定),转换若不可行,则保留文本类型存储,NULL或BLOB不做变化
INTEGER 将数据向整型转换,类似于NUMERIC,不同的是没有浮点标志的浮点数将转换为整型保存
REAL 将数据向浮点数类型转换,类似于NUMERIC,不同的是整数将转换为浮点数保存
NULL 不做任何转换的数据列类型

内部类型

请求的类型

转换

NULL

INTEGER

结果是0

NULL

FLOAT

结果是0.0

NULL

TEXT

结果是NULL

NULL

BLOB

结果是NULL

INTEGER

FLOAT

从整形转换到浮点型

INTEGER

TEXT

整形的ASCII码显示

INTEGER

BLOB

同上

FLOAT

INTEGER

浮点型转换到整形

FLOAT

TEXT

浮点型的ASCII显示

FLOAT

BLOB

同上

TEXT

INTEGER

使用atoi()

TEXT

FLOAT

使用atof()

TEXT

BLOB

没有转换

BLOB

INTEGER

先到TEXT,然后使用atoi

BLOB

FLOAT

先到TEXT,然后使用atof

BLOB

TEXT

如果需要的话添加0终止符



在iPhone中使用Sqlite 3主要步骤如下:
1 首先获取iPhone上Sqlite 3的数据库文件的地址
2 打开Sqlite 3的数据库文件
3 定义SQL文
4 邦定执行SQL所需要的参数
5 执行SQL文,并获取结果
6 释放资源
7 关闭Sqlite 3数据库。
// 首先获取iPhone上Sqlite3的数据库文件的地址  


NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);  
NSString *documentsDirectory = [paths objectAtIndex:0]; 
NSString *path = [documentsDirectorystringByAppendingPathComponent:@"database_name"]; 
// 打开Sqlite3的数据库文件  
sqlite3 *database;  
sqlite3_open([path UTF8String], &database); 
// 定义SQL文  
sqlite3_stmt *stmt;  
const char *sql = "SELECT * FROM table_name WHERE pk=? and name=?"; 
sqlite3_prepare_v2(database, sql, -1, &stmt, NULL); 
// 邦定第一个int参数  
sqlite3_bind_int(stmt, 1, 1);  
// 邦定第二个字符串参数  
sqlite3_bind_text(stmt, 2, [title UTF8String], -1,SQLITE_TRANSIENT);  
// 执行SQL文,并获取结果  
sqlite3_step(stmt);  
// 释放资源  
sqlite3_finalize(stmt);  
// 关闭Sqlite3数据库  
sqlite3_close(database); 


三、objective-c 与 Sqlite
1、SQLite的开发包
注意:要在工程中的Frameworks中导入相应的libsqlite3.dylib文件,也许在相应的目录下存在多个以libsqlite3开头的文件,务必选择libsqlite3.dylib,它始终指向最新版的SQLite3库的别名。 
libsqlite3.0.dylib文件地址:
/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk/usr/lib/libsqlite3.dylib
2、创建数据库
//数据库的位置
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];


//打开数据库 如果没有则会创建一个新的数据库
sqlite3 *sql;
if(sqlite3_open([[documentsDirectorystringByAppendingPathComponent:@"data.sqlite3"]UTF8String],&sql)!=SQLITE_OK) //data.sqlite3为自己定义数据库名称 
{
NSLog(@"创建数据库失败!");
return;
}
3、创建数据表
//创建一个新表 如果没有则会创一个新表
char *errorMsg;
NSString *createSQL = @"CREATE TABLE IF NOT EXISTS FIELDS (ROWINTEGER PRIMARY KEY, FIELD_DATA TEXT);";
if (sqlite3_exec (sql, [createSQL UTF8String],NULL, NULL,&errorMsg) != SQLITE_OK)
{
sqlite3_close(sql);
NSLog(@"创建数据表失败:%s",errorMsg);
}
4、插入更改数据
for (int i = 1; i <= 4; i++)
{
NSString *fieldName = [[NSString alloc] initWithFormat:@"field%d",i];
char *errorMsg;
char *update = "INSERT OR REPLACE INTO FIELDS (ROW, FIELD_DATA)VALUES (?, ?);";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(sql, update, -1, &stmt, nil) ==SQLITE_OK)
{
//列1 绑定数据
sqlite3_bind_int(stmt, 1, i);
//列2 绑定数据
sqlite3_bind_text(stmt, 2, [fieldName UTF8String], -1, NULL);
}
if (sqlite3_step(stmt) != SQLITE_DONE)
{
NSAssert1(0, @"插入数据产生错误: %s", errorMsg);
}
sqlite3_finalize(stmt);
}
5、查询数据
NSString *query = @"SELECT ROW, FIELD_DATA FROM FIELDS ORDER BYROW";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2( sql, [query UTF8String],-1, &statement,nil) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW)
{
//列1
int row = sqlite3_column_int(statement, 0);
//列2
char *rowData = http://www.mamicode.com/(char *)sqlite3_column_text(statement, 1);
NSString *fieldName = [[NSString alloc]
initWithFormat:@"%d", row];
NSString *fieldValue = http://www.mamicode.com/[[NSString alloc]
initWithUTF8String:rowData];


NSLog(@"Row:%@ Data:%@",fieldName,fieldValue);


}
sqlite3_finalize(statement);


//关闭数据库
sqlite3_close(sql);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
打开数据库 另外一种方法: 
NSArray *paths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);


    NSString *documentsDirectory= [paths objectAtIndex:0];


    NSString *path =[documentsDirectorystringByAppendingPathComponent:@"database.sqlite"];//database.sqlite为自己定义数据库名称


    NSFileManager *fileManager =[NSFileManager defaultManager];


    databasePath_ = path;


    BOOL find = [fileManagerfileExistsAtPath:path];


    if (!find) {


NSString *rePath = getBundleFilePath(@"database", @"sqlite");


NSData *dataFile = [NSData dataWithContentsOfFile:rePath];


[dataFile writeToFile:databasePath_ atomically:YES];


}
NSLog(@"Database file have already existed.");


if(sqlite3_open([path UTF8String], &database_) != SQLITE_OK){


sqlite3_close(database_);


NSLog(@"Error: open database file.");
}




四、简要说明一下SQLite数据库执行SQL语句的过程 


**调用sqlite3_prepare()将SQL语句编译为sqlite内部一个结构体(sqlite3_stmt).该结构体中包含了将要执行的的SQL语句的信息. 
** 如果需要传入参数,在SQL语句中用‘?‘作为占位符,再调用sqlite3_bind_XXX()函数将对应的参数传入.
**调用sqlite3_step(),这时候SQL语句才真正执行.注意该函数的返回值,SQLITE_DONE和SQLITE_ROW都是表示执行成功,不同的是SQLITE_DONE表示没有查询结果,象UPDATE,INSERT这些SQL语句都是返回SQLITE_DONE,SELECT查询语句在查询结果不为空的时候返回SQLITE_ROW,在查询结果为空的时候返回SQLITE_DONE. 
**每次调用sqlite3_step()的时候,只返回一行数据,使用sqlite3_column_XXX()函数来取出这些数据.要取出全部的数据需要反复调用sqlite3_step(). (注意,在bind参数的时候,参数列表的index从1开始,而取出数据的时候,列的index是从0开始). 
**在SQL语句使用完了之后要调用sqlite3_finalize()来释放stmt占用的内存.该内存是在sqlite3_prepare()时分配的. 
      **如果SQL语句要重复使用,可以调用sqlite3_reset()来清楚已经绑定的参数


五、构建一个SqliteHelper类及其方法(详细的类 见代码总结文件SQLiteHelper)


写一个SQLite的Helper文件,方便我们在其他的代码中使用,头文件(SqliteHelper.h)如下。
  #import 
  #import “sqlite3.h“
  #define kFileName @”mydatabase.sql”
  @interface SqliteHelper : NSObject {
  sqlite3 *database;
  }
  //创建表
  - (BOOL)createTable;
  //插入数据
  - (BOOL)insertMainTable:(NSString*) usernameinsertPassword:(NSString*) password;
  //查询表
  - (BOOL)checkIfHasUser;
  @end
  我们的代码文件如下。
  #import “SqliteHelper.h“
  @implementation SqliteHelper
  - (BOOL)createTable
  {
  NSArray *path =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
  NSString *paths = [[path objectAtIndex:0]stringByAppendingPathComponent:kFileName];
  NSFileManager *fileManager = [NSFileManagerdefaultManager];
  BOOL fileFinded = [fileManager fileExistsAtPath:paths];
  NSLog(@”Database file path is %@“,paths);
  if(fileFinded)
  {
  NSLog(@”Database file existed“);
  if(sqlite3_open([pathsUTF8String],&database)!=SQLITE_OK)
  {
  sqlite3_close(database);
  NSLog(@”Open Failed“);
  return NO;
  }
  }
  else
  {
  NSLog(@”Database file is not existed“);
  if(sqlite3_open([pathsUTF8String],&database)!=SQLITE_OK)
  {
  sqlite3_close(database);
  NSLog(@”Open Failed“);
  return NO;
  }
  }
  char *errorMsg;
  NSString *createSQL = @”create table if not exists fields (useridinteger primary key,username text,password text)“;
  if(sqlite3_exec(database,[createSQLUTF8String],NULL,NULL,&errorMsg)!=SQLITE_OK)
  {
  sqlite3_close(database);
  NSLog(@”Open failed or init filed“);
  return NO;
  }
  return YES;
  }
  - (BOOL)insertMainTable:(NSString*) usernameinsertPassword:(NSString*) password
  {
  char *errorMsg;
  NSString *createSQL = @”create table if not exists fields (useridinteger primary key,username text,password text)“;
  if(sqlite3_exec(database,[createSQLUTF8String],NULL,NULL,&errorMsg)!=SQLITE_OK)
  {
  sqlite3_close(database);
  NSLog(@”Open failed or init filed“);
  return NO;
  }
  NSString *insertData = http://www.mamicode.com/[[NSString alloc] initWithFormat:@”insert>  if(sqlite3_exec(database,[insertDataUTF8String],NULL,NULL,&errorMsg)!=SQLITE_OK)
  {
  sqlite3_close(database);
  NSLog(@”Open failed or failed to insert“);
  return NO;
  }
  return YES;
  }
  - (BOOL)checkIfHasUser
  {
  NSString *getUserCountSQL = @”select * from fields“;
  sqlite3_stmt *statement;
  NSLog(@”checkIfHasUser“);
  if(sqlite3_prepare_v2(database,[getUserCountSQLUTF8String],-1,&statement,nil)==SQLITE_OK)
  {
  //while(sqlite3_step(statement) == SQLITE_ROW)
  //{
  // int row = sqlite3_column_int(statement,0);
  // char* rowData = http://www.mamicode.com/(char*)sqlite3_column_text(statement,2);
  // NSString *fieldName = [[NSString alloc]initWithFormat:@”show%d”,row];
  // NSString *fieldValue = http://www.mamicode.com/[[NSString alloc]>  //
  // NSLog(@”fieldName is :%@,fieldValue is:%@”,fieldName,fieldValue);
  // return [[NSString alloc] initWithFormat:@”fieldName is:%@,fieldValue is :%@”,fieldName,fieldValue];
  //
  // [fieldName release];
  // [fieldValue release];
  //}
  //sqlite3_finalize(statement);
  if(sqlite3_step(statement) == SQLITE_ROW)
  {
  NSLog(@”Have user“);
  return YES;
  }
  }
  NSLog(@”No user“);
  return NO;
  }
  @end
  其中checkIfHasUser是检查数据,这个方法中我注释的是得到数据,因为我们这里只是check,所以不需要得到数据,直接看是否存在数据即可