Managing multiple queries at a time on sqlite3 database in iOS - ios

I have created two tables(vcards, viewdownloads) in sqlite3 database in iOS. At the beginning of my app starts Iam inserting my address book contacts to the sqlite3 database in to "vcards" table using background process.
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[self setNotificationForReachability];
dispatch_queue_t loadQueue = dispatch_queue_create("Image loader", NULL);
dispatch_async(loadQueue, ^{
// Your code to run in the background here
GVDBManager *objDB = [GVDBManager getSharedInstance];
[objDB getContactsFromAddBook];
[objDB syncPhoneBookWithVcardsTable];
});
}
in the middle of this process of inserting contacts into "vcards" table, I have called the another background process to update some values using update query in same "vcards" table
after this process completion again Iam calling one more background process to insert the some contacts to the "viewdownloads" table and displaying the contacts using UI.
my questions:
1.After click on one button, Reading the contacts from database and this is taking some time to load into array(aim reading 800 contacts). Iam using background process to do this.
2.Every time iam opening the database and closing it while inserting and updating the tables using background process.This causes my app crash and this is the major issue iam facing.
can any explain clearly how to handle multiple actions(inserting, updating and searching) on same database.
thanks in Advance.

1) Are you sure your method doesn't need to work on main thread? maybe some instruction need it.
2) Detect specific exeption ( or EXC_BAD_ACCESS ). I suggest you to enable All breakpoints. see image below:
3) Here main functions to open query and close db with sqlite. I use something like these without any problem
Sample function for open db connection
- (NSError *) openDatabase {
NSError *error = nil;
sqlite3* db = self.db; // temp ref
const char *dbpath = NULL;
dbpath = [self.databaseName UTF8String]; // disk-based db
// DB opening
int result = sqlite3_open(dbpath, &db);
if (result != SQLITE_OK) {
// const char *errorMsg = sqlite3_errmsg(db);
// ....
}else
self.db = db;
return error;
}
Sample function for close db connection
- (NSError *) closeDatabase {
NSError *error = nil;
if (self.db != nil) {
if (sqlite3_close(self.db) != SQLITE_OK){
// const char *errorMsg = sqlite3_errmsg(self.db);
// ...
}
self.db = nil;
}
return error;
}
Sample function for query DB using PDO (not SELECT clause)
- (NSError *)doPDOQuery:(NSString *)sql withParams:(NSArray *)params {
//NSError *closeError = nil;
__block NSError *errorQuery = nil;
dispatch_sync(self.fetchQueue, ^{
NSError *openError = nil;
//Check if database is open and ready.
if (self.db == nil) {
openError = [self openDatabase];
}
if (openError == nil) {
sqlite3_stmt *statement;
const char *query = [sql UTF8String];
sqlite3_prepare_v2(self.db, query, -1, &statement, NULL);
// BINDING
int count =0;
for (id param in params ) {
count++;
if ([param isKindOfClass:[NSString class]] )
sqlite3_bind_text(statement, count, [param UTF8String], -1, SQLITE_TRANSIENT);
if ([param isKindOfClass:[NSNumber class]] ) {
const char* paramObjType = [param objCType];
if (!strcmp(paramObjType, #encode(float))) // f
sqlite3_bind_double(statement, count, [param doubleValue]);
else if (!strcmp(paramObjType, #encode(double))) // d
sqlite3_bind_double(statement, count, [param doubleValue]);
else if (!strcmp(paramObjType, #encode(int))) // i
sqlite3_bind_int(statement, count, [param intValue]);
else if (!strcmp(paramObjType, #encode(unsigned int))) // I
sqlite3_bind_int(statement, count, [param unsignedIntValue]);
else if (!strcmp(paramObjType, #encode(char))) // c (eg: 1,0)
sqlite3_bind_int(statement, count, [param intValue]);
else if (!strcmp(paramObjType, #encode(long))) // q
sqlite3_bind_int64(statement, count, [param longLongValue]);
else if (!strcmp(paramObjType, #encode(unsigned long))) // Q
sqlite3_bind_int64(statement, count, [param unsignedLongLongValue]);
else
NSLog(#"SQLite binding - unknown NSNumber");
}
}
int result = sqlite3_step(statement);
if (result == SQLITE_ERROR ||
result == SQLITE_CONSTRAINT) {
// const char *errorMsg = sqlite3_errmsg(self.db);
// ...
}
sqlite3_finalize(statement);
}
else
errorQuery = openError;
});
return errorQuery;
}

Related

implementing WAL in iOS?

The code I placed below works. So my scenario is I have the code below in a class. I use this class simply for merging tables and updating one table. When I call the object that this code lives in the app delegate for example, it works great! But when I call the same object after clicking a button in a tableview controller, I get a database lock error. So here's what I am wondering. After reading sqlite documentation WAL: http://www.sqlite.org/wal.html I am thinking that I cannot read and update concurrently to the sqlite db, right? I am unsure of a solution around this, so what would be a good suggestion around my problem. Remember, keep in mind this code works just fine in the app delegate, it does not work in my tableview controller when I call it using action upon clicking the button. Note: I SELECT data from the SQLITE database to display table cell names etc. Thanks ahead of time!
//Allocates a filemanager object. Ideally, this object is used for searching through the applications context
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL dbErr = NO;
//Boolean variable to tell if the database exists
BOOL error,mainDbError;
//Looks through all the databases. If there is a database that does not exist, the following error message will appear to the user. If all the databases exist on the system, the database opens respectively
error = [self checkAndOpenSyncDB];
mainDbError = [self checkAndOpenMainDB];
if (!error&&mainDbError) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Oops!"
message:#"We didn't mean for this to happen. Looks like there was a problem loading the sync database. Contact technical support for further assistance."
delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
else{
//This is I think to check when the last time the sync occured on the system.. not quite sure though1
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSLog(#"Erorr syncing the database: Code: %d, message: '%s'", error,sqlite3_errmsg(syncOpenHandle));
char *errorMessage;
int errorNum = 0;
//Atataching the sync db to the master db
NSString *attachSQL = [NSString stringWithFormat:#"ATTACH DATABASE \'%#\' AS sync_db", self->pathForSync];
NSLog(#"PATH FOR SYNC !!!!!!! %#", pathForSync);
NSLog(#"Here's the arratch string: %#", attachSQL);
//
if (sqlite3_exec(syncOpenHandle, [attachSQL UTF8String], NULL, NULL, &errorMessage) == SQLITE_OK) {
NSString *masterQuery = [NSString stringWithFormat:#"SELECT name FROM sqlite_master WHERE type='table';"];
const char *masterStmt = [masterQuery UTF8String];
sqlite3_stmt *statement;
BOOL loopErr;
loopErr = sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL);
if (sqlite3_prepare_v2(syncOpenHandle, masterStmt, -1, &statement, NULL)==SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
NSString * currentTable = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
NSLog(#"Here's the current table: %#",currentTable);
//This is where the magic happens. If there are any keys matching the database, it will update them. If there are no current keys in the database, the query will insert them.
if ([currentTable isEqualToString:#"USER_DATA"] == NO && [currentTable isEqualToString:#"USER_ACTIVITY"]== NO && [currentTable isEqualToString:#"USER_ITINERARY"] == NO) {
NSString *tblUpdate = [NSString stringWithFormat:#"INSERT or REPLACE INTO main.%# SELECT * FROM sync_db.%#;",currentTable, currentTable];
const char *updateStmt = [tblUpdate UTF8String];
//sqlite3_busy_timeout (mainOpenHandle, 60000);
bool update;
update = sqlite3_exec(syncOpenHandle, updateStmt, NULL, NULL, &errorMessage)== SQLITE_OK;
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
if (sqlite3_exec(syncOpenHandle, updateStmt, NULL, NULL, &errorMessage)== SQLITE_OK) {
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
if (errorNum == 1) {
//A database reset is needded
//self->isResetDataBase = YES;
}
dbErr = YES;
}
}
}
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
}
NSLog(#"Here's the error num %d", errorNum);
NSLog(#"Erorr syncing the database: Code: %d, message: '%s'", error,sqlite3_errmsg(syncOpenHandle));
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
sqlite3_finalize(statement);
//Detaching the database from the mainDB
NSString *detachSQL = [NSString stringWithFormat:#"DETACH DATABASE sync_db"]; // reference sync db
if ((errorNum = sqlite3_exec(syncOpenHandle, [detachSQL UTF8String], NULL, NULL, &errorMessage))!= SQLITE_OK) {
NSLog(#"Detatched syncDb Failed. ErrorMessage = %s ", errorMessage);
}
}
}
NSLog(#"Error sync ... '%s'", sqlite3_errmsg(syncOpenHandle));
//Closing the database when finished.
if (syncOpenHandle!=nil) {
sqlite3_close(syncOpenHandle);
NSError *err;
int success = [fileManager fileExistsAtPath:pathForSync];
if (success) {
[[NSFileManager defaultManager]removeItemAtPath:pathForSync error:&err];
}
}

Sqlite - while (sqlite3_step(statement) == SQLITE_ROW) is not executing

I am trying to get the highest value in the Points column in my DB, however the sqlite_step statement never get executed.
This is the method with this in it.
+(NSMutableArray*)getMax {
[self databaseInit];
highScore *newMax = [[highScore alloc]init];
NSMutableArray *maxPointsArray = [[NSMutableArray alloc]init];
if (sqlite3_open(dbpath,&peopleDB)==SQLITE_OK)
{
NSString *selectMaxSQL = [NSString stringWithFormat:#"SELECT MAX POINTS FROM PEOPLE"];
if(sqlite3_prepare_v2(peopleDB, [selectMaxSQL UTF8String],-1,&statement, NULL)==SQLITE_OK){
while (sqlite3_step(statement) == SQLITE_ROW){
newMax.max = sqlite3_column_int(statement, 0);
}
[maxPointsArray addObject:newMax];
}
}
sqlite3_finalize(statement);
sqlite3_close(peopleDB);
return maxPointsArray;
}
If it makes a difference the databaseInit method is -
+(void)databaseInit {
//get documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths [0];
//create path to database file
databasePath = [[NSString alloc]initWithString:[docsDir stringByAppendingPathComponent:#"people.db"]];
dbpath = [databasePath UTF8String];
}
Any help with this would be hugely appreciated, let me know if any more code is needed.
It is not executed because your query is wrong.
Assuming that points is a field of your people table, the query should be:
SELECT MAX(points) FROM people
You have the immediate answer, but the long term solution is to log sqlite3_errmsg if any SQLite functions fail. The way your code is written, you have no way of knowing which of the calls failed and why.
Thus, you may want:
+(NSMutableArray*)getMax {
int rc;
[self databaseInit];
highScore *newMax = [[highScore alloc]init];
NSMutableArray *maxPointsArray = [[NSMutableArray alloc]init];
if ((rc = sqlite3_open(dbpath, &peopleDB)) == SQLITE_OK) {
const char *selectMaxSQL = "SELECT MAX(POINTS) FROM PEOPLE";
if(sqlite3_prepare_v2(peopleDB, selectMaxSQL, -1, &statement, NULL)==SQLITE_OK){
if ((rc = sqlite3_step(statement)) == SQLITE_ROW){
newMax.max = sqlite3_column_int(statement, 0);
[maxPointsArray addObject:newMax];
} else {
if (rc == SQLITE_DONE) {
NSLog(#"No record returned");
} else {
NSLog(#"Step failed %s (%d)", sqlite3_errmsg(peopleDB), rc);
}
}
sqlite3_finalize(statement);
} else {
NSLog(#"Prepare failed %s (%d)", sqlite3_errmsg(peopleDB), rc);
}
} else {
NSLog(#"Open failed %d", rc);
}
sqlite3_close(peopleDB);
return maxPointsArray;
}
If you had done this with your original SQL, it would have reported precisely where in the SQL it encountered the error. Furthermore, if you have any problems in the future, this sort of defensive programming will help diagnose problems more quickly.
It's also worth noting that it's generally recommended not to open and close the database with every database call, but rather to open the database only once. It's more efficient and people have reported problems when constantly opening and closing databases.

How to retrieve value from SQLite database

I'm trying to get data from my DB but I have some problem.
Here is my code:
NSString *action=[[NSString alloc]init];
NSString *queryStatement = [NSString stringWithFormat:#"SELECT ACTIONNAME FROM ACTIONS WHERE ACTIONSYMBOL = '%#'", symbol];
// Prepare the query for execution
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, [queryStatement UTF8String], -1, &statement, NULL) == SQLITE_OK)
{
// Create a new address from the found row
while (sqlite3_step(statement) == SQLITE_ROW) {
action = [NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 1)]; // fails on this line
}
sqlite3_finalize(statement);
return action;
}
(the parameter symbol come from outside)
When I run this, it fails at the line with the call to stringWithUTF8String with sqlite3_column_text results.
You probably want to collect the results in an NSMutableArray:
NSMutableArray *action = [[NSMutableArray alloc] init];
...
while (sqlite3_step(statement) == SQLITE_ROW) {
[action addObject:[NSString stringWithUTF8String:(char*)sqlite3_column_text(statement, 0)]];
}
...
You can then see what was collected later:
for (NSString *s in action) {
NSLog(#"%#", s);
}
EDIT As pointed out in #Rob's answer, the first column is 0, not 1.
In your call to sqlite3_column_text, you're using the index 1, but it takes a zero-based index. Use 0 instead of 1. See the SQLite sqlite_column_XXX documentation, which says:
The leftmost column of the result set has the index 0.
By the way, since stringWithUTF8String throws an exception if you pass it a NULL value, it's often safer to check the result if sqlite3_column_text is not NULL before proceeding, and handle the error gracefully otherwise. Also, you might want to check for sqlite3_step and sqlite3_prepare_v2 errors, like so:
NSString *queryStatement = [NSString stringWithFormat:#"SELECT ACTIONNAME FROM ACTIONS WHERE ACTIONSYMBOL = '%#'", symbol]; // note, it can be dangerous to use `stringWithFormat` to build SQL; better to use `?` placeholders in your SQL and then use `sqlite3_bind_text` to bind the `symbol` value with the `?` placeholder
if (sqlite3_prepare_v2(database, [queryStatement UTF8String], -1, &statement, NULL) == SQLITE_OK)
{
int rc;
while ((rc = sqlite3_step(statement)) == SQLITE_ROW) {
const unsigned char *value = sqlite3_column_text(statement, 0); // use zero
if (value) {
NSString *action = [NSString stringWithUTF8String:(const char *)value];
// now do whatever you want with `action`, e.g. add it to an array or what
} else {
// handle the error (or NULL value) gracefully here
}
// make sure to check for errors in `sqlite3_step`
if (rc != SQLITE_DONE)
{
NSLog(#"%s: sqlite3_step failed: %s", __FUNCTION__, sqlite3_errmsg(database));
}
}
}
else
{
NSLog(#"%s: sqlite3_prepare_v2 failed: %s", __FUNCTION__, sqlite3_errmsg(database));
}
Incidentally, as the above illustrates, to correctly perform all of the error checking is a little cumbersome. This is where FMDB can be useful, simplifying the above to (where db is an FMDatabase object that has been opened):
FMResultSet *rs = [db executeQuery:#"SELECT ACTIONNAME FROM ACTIONS WHERE ACTIONSYMBOL = ?", symbol];
if (!rs) {
NSLog(#"%s: executeQuery failed: %#", __FUNCTION__, [db lastErrorMessage]);
return;
}
while ([rs next]) {
NSString *action = [rs stringForColumnIndex:0];
// do whatever you want with `action` here
}
[rs close];
And if you use ? placeholders (rather than using stringWithFormat to build your SQL, which is dangerous) the benefits of using FMDB are even more compelling.

Background task with Completion block in IOS

I am doing some database operations in IOS. Basically I want to do this in a background thread. I tried using GCD. But the issue for me is I want to get some values from this process after it is finished. Say before inserting an item to database I check whether the item already exists. Please see code below
__block Boolean isExisting = false;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
NSString *path = [SqliteManager initDatabase];
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
NSString *query = [NSString stringWithFormat:#"select count (campaignId) from POTC where Id='%#' and taskid='%#' and pocId='%#' and userId='%#'",[submission.campaignId stringRepresentation],[submission.taskId stringRepresentation],[submission.pocId stringRepresentation],[[UUID UUIDWithString:submission.userId] stringRepresentation]];
const char *sql = [query cStringUsingEncoding:NSASCIIStringEncoding];
sqlite3_stmt *selectStatement;
if (sqlite3_prepare_v2(database, sql, -1, &selectStatement, NULL) == SQLITE_OK)
{
while (sqlite3_step(selectStatement) == SQLITE_ROW)
{
if (sqlite3_column_int(selectStatement, 0) >0)
{
isExisting = true;
break;
}
}
sqlite3_finalize(selectStatement);
}
sqlite3_close(database);
}
return isExisting;
});
But the above code with return statement wont work as dispatch-async is expecting a void code block. How can i achieve the same in IOS? Is there something similar to animation completion block in IOS?
The block has to have a return type of void because there is nowhere to return the value to in an asynchronous block.
The variable isExisting is qualified __block which means it will be set whenever the block assigns to it. Unfortunately, your main thread won't have access to it once it has exited the scope. The wary to do this is for your block to call another method (or function, or block) that sets a variable or property that you know will still be around when the asynchronous block has finished.
e.g. you could have a method on the app delegate to invoke on completion.
// in your appDelegate implementation
-(void) updateUIAfterDatabaseUpdate: (bool) isExisting
{
if (isExisting)
{
// e.g. display an error
}
else
{
// successful update
}
}
// The update code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
bool isExisting = false;
NSString *path = [SqliteManager initDatabase];
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
// Snipped for clarity
}
dispatch_async(dispatch_get_main_queue(), ^(void) {
[appDelegate updateUIAfterDatabaseUpdate: isExisting] ;
});
});
The dispatch on the main queue ensures that the method is called in the main thread so it can do UI updates.
Maybe you should create a function with a completion block.
I define mines like this :
typedef void (^myBlock)(type1 param1,
type2 param2);
-(void)myAsyncFunctionWithParam:(id)paramSend withCompletionBlock:(myBlock)returningBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//here everything you want to do
// especially defining 2 parameters to return (let them be p1 & p2)
dispatch_async(dispatch_get_main_queue(), ^{
returningBlock(p1,p2);
});
});
}
and you can use it like :
[self myAsyncFunctionWithParam:ps
withCompletionBlock:^(type1 paramToUse1,
type2 paramToUse2)
{
//You can use here paramToUse1 and paramToUse2
}
];
You can use whatever type you want for type in the block : NSString, NSDate,... (don't forgive * if needed)
You don't have to return something because isExisting will become true and if you access its value after completion of block execution, it returns true.
The way you are thinking loses the benefit of the background thread :). You should restructure your program to fit better with the asynchronous paradigm. You launch your background work asynchronously and then when it's finished then you can send a message to a receiver to do another work. In your code for example you can use notification or a simple message send. like this :
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
NSString *path = [SqliteManager initDatabase];
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
NSString *query = [NSString stringWithFormat:#"select count (campaignId) from POTC where Id='%#' and taskid='%#' and pocId='%#' and userId='%#'",[submission.campaignId stringRepresentation],[submission.taskId stringRepresentation],[submission.pocId stringRepresentation],[[UUID UUIDWithString:submission.userId] stringRepresentation]];
const char *sql = [query cStringUsingEncoding:NSASCIIStringEncoding];
sqlite3_stmt *selectStatement;
if (sqlite3_prepare_v2(database, sql, -1, &selectStatement, NULL) == SQLITE_OK)
{
while (sqlite3_step(selectStatement) == SQLITE_ROW)
{
if (sqlite3_column_int(selectStatement, 0) >0)
{
// Here you can send the notification with the data you want.
break;
}
}
sqlite3_finalize(selectStatement);
}
sqlite3_close(database);
}
return isExisting;
});

sqlite iphone best practice for reading data

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 will 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;
}

Resources