I'm trying to make an app, that reads from an SQLite3 database. I plan to pre-load data during development, so the app does not need to modify anything in the database, only read from it, make queries, etc.
What is the best practice for solely reading data? Should I open the database, read the data, and close it, with each query? The app w开发者_StackOverflow中文版ill be making many small queries and a few large ones. Is it better to have the database open for the duration of the app, or open/close it with each fetch?
Reading:
1. For queries, it's important to re-use compiled statements.
2. Make sure you use parameters so you can re-use those compiled queries
When you call sqlite3_prepare_v2, it compiles the statement and gives you a reference to the statement back. Find a way to save that off and re-use it. See the code below for *statement. You pass &statement into prepare.
Also, note the use of ? for parameters. If you're going to re-use the statement, it's important to call sqlite3_reset() againt the statement, rebind the inputs from the program (parameters) and execute it again.
sqlite3_stmt *statement;
NSString *querySQL = @"update contacts set name=?,address=?,phone=? where id=?";
NSLog(@"query: %@", querySQL);
const char *query_stmt = [querySQL UTF8String];
// preparing a query compiles the query so it can be re-used.
// find a way to save off the *statement so you can re-use it.
sqlite3_prepare_v2(_contactDb, query_stmt, -1, &statement, NULL);
// use sqlite3_bind_xxx functions to bind in order values to the params
sqlite3_bind_text(statement, 1, [[contact name] UTF8String], -1, SQLITE_STATIC);
sqlite3_bind_text(statement, 2, [[contact address] UTF8String], -1, SQLITE_STATIC);
sqlite3_bind_text(statement, 3, [[contact phone] UTF8String], -1, SQLITE_STATIC);
sqlite3_bind_int64(statement, 4, [[contact id] longLongValue]);
Always check the return codes! and log or handle the errors.
rc = sqlite3_step(stmt);
switch (rc)
{
case SQLITE_ROW:
// ...
break;
case SQLITE_OK:
case SQLITE_DONE:
break;
default:
// ....
}
return NO;
}
if you get an error, log or get the eror message to provide more info:
- (NSString*)errorMessage
{
return [NSString stringWithCString:sqlite3_errmsg(_sqlite3) encoding:NSUTF8StringEncoding];
}
Use sqlite_open_v2
with the SQLITE_OPEN_READONLY
flag. For example, I use the following method to open a database for reading only.
// Open for reading only.
- (int) openDatabaseAtPath:(NSString *) path
{
if (database != nil)
{
sqlite3_close(self.database);
[self setDatabase:nil];
}
int errorCode = SQLITE_OK;
errorCode = sqlite3_open_v2([path UTF8String],
&database,
SQLITE_OPEN_READONLY,
NULL);
return errorCode;
}
Unless you copy the database to the Documents directory, you work with the DB from the resource dir, and that one is readonly.
When you open the database using sqlite_open_v2
and the SQLITE_OPEN_READONLY
flag, SQLite opens the file itself in read-only mode, so even if your application, due to a bug, corrupts memory belonging to SQLite, the database will stay untouched.
With this in mind, I'd keep the database open until the application quits. (You may wish to close it if you receive a low-memory notification and reopen it on demand, but opening and closing it for every query would be wasteful.)
As per you question you want to read data from database. So following are the answer of you questions.
- No need to open db each time when you fire the query. Just one it one time. It is better if you create singleton class and open db in it for first time when it initiate.
- Use following code method which will work for all select queries which has conditional select, group by etc.
- I) it takes Output/result column names as input array ii ) TableName iii) Where condition iv)OrderBy clause v)group By clause
- (NSMutableArray *)runSelecteQueryForColumns: (NSArray *)p_columns ontableName: (NSString *)p_tableName withWhereClause: (NSString *)p_whereClause withOrderByClause: (NSString *)p_orederByCalause withGroupByClause: (NSString *)p_groupByClause
{
NSMutableArray *l_resultArray = [[NSMutableArray alloc] init];
if(!self.m_database)
{
if(![self openDatabase])
{
sqlite3_close(self.m_database);
//NSLog(@"error in select : DB creating : %@",p_whereClause);
return nil;
}
}
NSMutableString *l_simpleQuery =[[NSMutableString alloc] initWithString:@"Select"] ;
if(p_columns)
{
for(int l_row = 0 ; l_row < [p_columns count] ; l_row++)
{
if(l_row != [p_columns count]-1)
{
[l_simpleQuery appendString:[NSString stringWithFormat:@" %@,", [p_columns objectAtIndex:l_row]]];
}
else
{
[l_simpleQuery appendString:[NSString stringWithFormat:@" %@", [p_columns objectAtIndex:l_row]]];
}
}
}
else
{
[l_simpleQuery appendString:@" *"];
}
[l_simpleQuery appendString:[NSString stringWithFormat:@" From %@",p_tableName]];
if(p_whereClause)
{
[l_simpleQuery appendString:[NSString stringWithFormat:@" %@",p_whereClause]];
}
if(p_groupByCaluase)
{
[l_simpleQuery appendString:[NSString stringWithFormat:@" %@",p_groupByCaluase]];
}
if(p_orederByCalause)
{
[l_simpleQuery appendString:[NSString stringWithFormat:@" %@",p_orederByCalause]];
}
//NSLog(@"Select Query: - %@",l_simpleQuery);
const char *l_query_stmt = [l_simpleQuery UTF8String];
sqlite3_stmt *l_statement = nil;
int i = sqlite3_prepare_v2(self.m_database,
l_query_stmt, -1, &l_statement, NULL);
if (i == SQLITE_OK)
{
while(sqlite3_step(l_statement) == SQLITE_ROW)
{
[l_resultArray addObject:[self createDictionary:l_statement]];
}
sqlite3_finalize(l_statement);
}
else
{
sqlite3_finalize(l_statement);
//sqlite3_close(l_database);
DDLogError(@"%@ - error in SQL :%@",THIS_FILE,l_simpleQuery);
return nil;
}
//NSLog(@"RESULT %@",l_resultArray);
return l_resultArray;
}
精彩评论