I am using the code below to create db in one class and using it in the viewController and same code again in another class in newViewController .
Is it correct ? Or does it create new db in its place or uses if there is an existing db ?
or Do I need to use only the path ?
When I tried extern NSString *path in the example below I received Chinese letters and could not open it.
Basically in the 2nd contoller I am only reading the data.
So I am trying to access db via path,database with path and database open.
How do it if I have used the code below to create it and I would like to open it 2nd time from another viewcontoller.
Please help.
Thanks in Advance.
In 1st Contoller:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *path = [docsPath stringByAppendingPathComponent:#"test.db"];
FMDatabase *database;
database = [FMDatabase databaseWithPath:path];
[database open];
[database executeUpdate:#"DROP TABLE IF EXISTS **"];
[database executeUpdate:#"CREATE TABLE "];
//Select query for single row
FMResultSet *s = [database executeQuery:#"SELECT COUNT(*) FROM table"];
if ([s next]) {
int Count = [s intForColumnIndex:0];
}
//DO Something
[database close];
Above code is working perfectly in viewcontroller 1 .
Things I tried in view controller 2 .And it still did not work.
extern NSString *path;
FMDatabase *database;
database = [FMDatabase databaseWithPath:path];
[database open];
I get an error here since the path is in What looks like Chinese language.
Error is EXC_BAD_ACCESS code .
Also tried global database by :
extern FMDatabase *database;
and using it as
[database open];
I still get the same error.
Finally it worked with the following code in the 2nd controller.
I just wanted to see if thats the right way to do it or can I use something as db open .
But I tried that and its not working.Also I am not using any error handler as I dont know how to do it in FMDB .
Thanks in Advance.
Please let me know if the following implementation is correct ? :
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *newpath = [docsPath stringByAppendingPathComponent:#"test.db"];
FMDatabase *database2 ;
database2 = [FMDatabase databaseWithPath:newpath];
[database2 open];
//DO Something
[database2 close];
As an example of how you should be checking for the success of the FMDB methods, here is an example:
#import "FMDatabase.h"
#import "FMDatabaseAdditions.h"
- (void)performDatabaseDemonstration
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *path = [docsPath stringByAppendingPathComponent:#"test.db"];
// open db
FMDatabase *database = [FMDatabase databaseWithPath:path];
FMResultSet *rs;
if ([database open])
{
// drop table
if (![database executeUpdate:#"DROP TABLE IF EXISTS test"])
NSLog(#"%s: drop error: %#", __FUNCTION__, [database lastErrorMessage]);
// create table
if (![database executeUpdate:#"CREATE TABLE test(col1 INTEGER, col2 TEXT)"])
NSLog(#"%s: create error: %#", __FUNCTION__, [database lastErrorMessage]);
// insert a row of data
if (![database executeUpdate:#"INSERT INTO test VALUES(1, 'Rob')"])
NSLog(#"%s: insert 1 error: %#", __FUNCTION__, [database lastErrorMessage]);
// insert another row of data
if (![database executeUpdate:#"INSERT INTO test VALUES(2, 'Rachel')"])
NSLog(#"%s: insert 2 error: %#", __FUNCTION__, [database lastErrorMessage]);
// Select query for single row
if (!(rs = [database executeQuery:#"SELECT count(*) FROM test"]))
NSLog(#"%s: select error: %#", __FUNCTION__, [database lastErrorMessage]);
if ([rs next]) {
int count = [rs intForColumnIndex:0];
NSLog(#"%s: count = %d", __FUNCTION__, count);
}
[rs close];
// if getting single value from single row, you can use FMDatabaseAdditions.h methods:
NSInteger count2 = [database intForQuery:#"SELECT col1, col2 FROM test"];
NSLog(#"%s: count2 = %d", __FUNCTION__, count2);
// let's actually retrieve data
if (!(rs = [database executeQuery:#"SELECT col1, col2 FROM test"]))
NSLog(#"%s: select error: %#", __FUNCTION__, [database lastErrorMessage]);
while ([rs next]) {
NSLog(#"%s: col1 = %#; col2 = %#", __FUNCTION__, [rs objectForColumnIndex:0], [rs objectForColumnIndex:1]);
}
[rs close];
//close
[database close];
}
else
{
NSLog(#"%s: open error: %#", __FUNCTION__, [database lastErrorMessage]);
}
}
In terms of how to open a database from different controllers there are a ton of options. But I might suggest that above and beyond just retrieving some shared path/filename variable from two controllers, you might want to actually encapsulate not only the filename, but shared FMDB code, too, in an object.
Personally, whenever I find myself writing the same or similar code in two different controllers, I ask myself whether I want to abstract that code out into its own class. In this case, for example, rather than debating how these two classes access the database filename, I would create my own own model object (a NSObject subclass), that encapsulates your SQL, does the opening of the database, etc. Then I could have my two view controllers invoke the methods from that method object, and really minimize not only redundancy in the filenames, but in the code itself, too. For example, the creating of the database path, opening the database, checking the success of the open, etc., are identical. So why not have one method in some model object, that does all of that for you.
To make this model object accessible from both controllers, you have a bunch of options:
Make it a property of your first view controller, and have it pass it to your second controller during prepareForSegue;
Create a singleton object for it; or
Make that a property of your app delegate, and both of your controllers can get it from there.
You should put the database path defined as global variables, the first time to use the closing operation, the second use of time to open, open the database, it will automatically judge whether there are database, if there is no will automatically create.If present, it will directly open the. Used you to judge whether the database open success:
if (! [db open]) {
/ / error
return;
}
/ / some operation
/ /...
[db close];
Related
I have more than 10000+ record in database. I want to load it on a table view. It will take 3 to 4 seconds to fetch all data from database and than load it on table view. Is there is more efficient way to increase the response and load the data in a uitableview?
Here is my code for get All Data fro database
- (void)getAllData {
NSString * convertInttoStr = [NSString stringWithFormat:#"%d", rowNumber];
// Getting the database path.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *dbPath = [docsPath stringByAppendingPathComponent:#"iandroidquran_database 3.sqlite"];
FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
NSString *sqlSelectQuery = [NSString stringWithFormat:
#"SELECT * FROM qSurahContent WHERE surahID=%#" ,
convertInttoStr];
// Query result
FMResultSet *resultsWithNameLocation = [database executeQuery:sqlSelectQuery];
while([resultsWithNameLocation next]) {
NSString *strID = [NSString stringWithFormat:#"%d",[resultsWithNameLocation intForColumn:#"surahID"]];
NSString *strName = [NSString stringWithFormat:#"%#",[resultsWithNameLocation stringForColumn:#"surahContentArabic"]];
NSLog(#"surahID = %#, surahName = %#",strID, strName);
[surahId addObject:strID];
[surahContentArabic addObject:strName];
}
[self.tblView reloadData];
[database close];
}
Any solution?? Thanks
Try to fetch Data from DB in main Thread as following :
dispatch_async(dispatch_get_main_queue(), ^{
[self getAllData];
});
This main thread has high priority and will execute faster than ever. So try this one.
Hope it helps..
Here is the code which I'm using to insert a record into a sqlite table
FMDatabase *dataBase = [self openDatabase];
[dataBase open];
if ([dataBase open] != YES) {
NSLog(#"DB Error %d: %#", [dataBase lastErrorCode], [dataBase lastErrorMessage]);
//VERY IMPORTANT
}
BOOL success= [dataBase executeUpdate:#"Insert into CrewUnits (pkCrewUnits,fkUnits,fkAgencyVehicles,fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, PrimaryUnit) values (?, ?, ?, ?, ?, ?);", pkCrewUnits, fkUnits, fkAgencyVehicle, fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, [NSNumber numberWithInt:1]];
NSLog(success ?#"YES" :#"NO");
NSLog(#"Error %d: %#", [dataBase lastErrorCode], [dataBase lastErrorMessage]);
FMResultSet*resultSet= [dataBase executeQuery:#"select * from CrewUnits"];
NSLog(#"ResultSet : %#", [resultSet resultDictionary]);
[dataBase close];
Database Path
- (FMDatabase *)openDatabase
{
NSLog(#"Open Database");
NSString *documents_dir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *db_path = [documents_dir stringByAppendingPathComponent:[NSString stringWithFormat:#"HPSix_05BD.db"]]; // DatabasePath
FMDatabase *db = [FMDatabase databaseWithPath:db_path];
if (![db open])
NSLog(#"Failed to open database!!!!!");
return db;
}
I'm using the same logic to fetch data from table that works fine for me. But I can't insert a record. I don't know what I'm doing wrong here.
Two problems:
You are calling open three times. Once in openDatabase and twice in the code that called it.
So, have your open routine open the database if it can, but return nil if it can't:
- (FMDatabase *)openDatabase
{
NSLog(#"Open Database");
NSString *documents_dir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *db_path = [documents_dir stringByAppendingPathComponent:[NSString stringWithFormat:#"HPSix_05BD.db"]]; // DatabasePath
FMDatabase *db = [FMDatabase databaseWithPath:db_path];
if (![db open]) {
NSLog(#"Failed to open database!!!!!");
return nil;
}
return db;
}
Then check that result:
FMDatabase *dataBase = [self openDatabase];
if (!dataBase) {
// quit; note, no point in checking error message as it only returns meaningful messages if you have an open database
}
After calling executeQuery, you have to call next.
FMResultSet *resultSet = [dataBase executeQuery:#"select * from CrewUnits"];
if ([resultSet next]) {
NSLog(#"ResultSet : %#", [resultSet resultDictionary]);
} else {
NSLog(#"No data found");
}
I think I see the problem. You are committing after running the SELECT query.
You have to commit right after the UPDATE query. When you do it like you have now when selecting, the UPDATE query has not been committed and thus will not be returned.
[dataBase beginTransaction];
BOOL success= [dataBase executeUpdate:#"Insert into CrewUnits (pkCrewUnits,fkUnits,fkAgencyVehicles,fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, PrimaryUnit) values (?, ?, ?, ?, ?, ?);", pkCrewUnits, fkUnits, fkAgencyVehicle, fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, [NSNumber numberWithInt:1]];
[dataBase commit]; // commit the query
NSLog(success ?#"YES" :#"NO");
NSLog(#"Error %d: %#", [dataBase lastErrorCode], [dataBase lastErrorMessage]);
// now at this point the transaction has been committed and can be selected
FMResultSet*resultSet= [dataBase executeQuery:#"select * from CrewUnits"];
NSLog(#"ResultSet : %#", [resultSet resultDictionary]);
Also according to FMDB docs:
The parameters must start with a colon. SQLite itself supports other
characters, but internally the Dictionary keys are prefixed with a
colon, do not include the colon in your dictionary keys.
Thus this line of code must be rewritten to:
BOOL success= [dataBase executeUpdate:#"Insert into CrewUnits (pkCrewUnits,fkUnits,fkAgencyVehicles,fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, PrimaryUnit) values (:pkCrewUnits,:fkUnits,:fkAgencyVehicles,:fkLookupCodes_PrimaryRole, :fkLookupCodes_LevelOfCare, :PrimaryUnit);", pkCrewUnits, fkUnits, fkAgencyVehicle, fkLookupCodes_PrimaryRole, fkLookupCodes_LevelOfCare, [NSNumber numberWithInt:1]];
Lastly according to the docs you use next for looping through the results:
while ([resultSet next]) {
//retrieve values for each record
}
I use FMDB in my iOS project. But when I read ROWID with FMDB,the Xcode log "Warning: I could not find the column named 'rowid'."
...
//Create database
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *dbPath = [documentsPath stringByAppendingPathComponent:#"bookmarksDatabase.sqlite"];
BOOL needCreateTable = ![[NSFileManager defaultManager] fileExistsAtPath:dbPath];
FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
[db open];
if (needCreateTable) {
[db executeUpdate:#"CREATE TABLE bookmarks (title TEXT ,url TEXT ,folderID INTEGER ,locationIndex INTEGER)"];
}
else
{
[self reloadBookmarkDatabase:db];
}
[db close];
...
//Read database. Only "ROWID" column can't find.
FMResultSet *results = [db executeQuery:#"SELECT * FROM bookmarks ORDER BY locationIndex"];
while([results next]) {
BookmarkData *temp = [[BookmarkData alloc] initWithID:[results intForColumn:#"ROWID"] title:[results stringForColumn:#"title"] url:[results stringForColumn:#"url"] folderID:[results intForColumn:#"folderID"] locationIndex:[results intForColumn:#"locationIndex"]];
[self.bookmarkArray addObject:temp];
NSLog(#"bookmark id:%d, title:%#, url:%#, folderID:%d, locationIndex:%d",temp.ID,temp.title,temp.url,temp.folderID,temp.locationIndex);
}
SELECT * returns only the columns from the table definition.
If you want to get the rowid column, you have to list it explicitly:
SELECT *, rowid FROM ...
And if you actually need to use this column, it would be a better idea to make it part of the table definition:
CREATE TABLE bookmarks (
ID INTEGER PRIMARY KEY, -- same as rowid
title TEXT,
...
)
I'm a Ios developer and i'm having a problem with attaching 2 SQLite db's .
We are getting this error when running:
Error calling sqlite3_step (1: SQL logic error or missing database) SQLITE_ERROR
2012-06-19 11:10:19.658 °°°°°°°[453:707] DB Query: ATTACH DATABASE '/var/mobile/Applications/A05034A8-F5AF-45AA-B520-DF2BBEBB800B/Documents/Meals.db' AS mealDB
2012-06-19 11:10:19.672 °°°°°°°[453:707] Err 1: no such table: Meal
2012-06-19 11:10:19.680 °°°°°°°[453:707] Error calling sqlite3_step (1: SQL logic error or missing database) SQLITE_ERROR
2012-06-19 11:10:19.684 °°°°°°°[453:707] DB Query: ATTACH DATABASE '/var/mobile/Applications/A05034A8-F5AF-45AA-B520-DF2BBEBB800B/Documents/Meals.db' AS mealDB
2012-06-19 11:10:19.693 °°°°°°°[453:707] Err 1: no such table: MealItem
2012-06-19 11:10:19.698 °°°°°°°[453:707] ERREUR
After attaching, we are trying to collect data . He is not finding the table we are searching in: Err 1: no such table: MealItem or Err 1: no such table: Meal.
It gets even stranger!
When we test the same code in Sim with iOS 5.0 it works !
Data is returning from the request!
Still getting this error a few times though :
Error calling sqlite3_step (1: SQL logic error or missing database) SQLITE_ERROR
2012-06-19 11:10:19.684 °°°°°°°[453:707] DB Query: ATTACH DATABASE '/var/mobile/Applications/A05034A8-F5AF-45AA-B520-DF2BBEBB800B/Documents/Meals.db' AS mealDB
We tried attaching the 2 databases in sqlight manager, and this was successful.
This result database also worked in the application .
It seems like iOS has a problem attaching databases .
Or we made an error somewhere .
We are searching for a solution without any luck so far.
Here some code :
(Using FMDatabase api)
- (FMDatabase *) attachMealDatabase:(FMDatabase *) db {
if (![db open]) {
NSLog(#"Could not open db.");
return nil;
}else {
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
//const char * mainDBPath = [[documentDirectory stringByAppendingPathComponent:#"Main.db"] UTF8String];
const char * mealDBPath = [[documentDirectory stringByAppendingPathComponent:#"Meals.db"] UTF8String];
NSString *attachSQL = [NSString stringWithFormat: #"ATTACH DATABASE \'%s\' AS mealDB", mealDBPath];
[db beginTransaction];
[db executeUpdate:attachSQL];
[db commit];
//[db close];
return db;
}
Try attaching the database in the FMDatabase layer instead of the viewcontrollers.
We did it in the Db open function by calling the following function:
-(void)attachDb{
NSArray paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString documentDirectory = [paths objectAtIndex:0];
const char secondDBPath = [[documentDirectory stringByAppendingPathComponent:#"Second.db"] UTF8String];
if (sqlite3_open([databasePath UTF8String], &db) == SQLITE_OK)
{
NSString *strSQLAttach = [NSString stringWithFormat:#"ATTACH DATABASE \'%s\' AS SECOND", secondDBPath ];
char *errorMessage;
if (sqlite3_exec(db, [strSQLAttach UTF8String], NULL, NULL, &errorMessage) == SQLITE_OK)
{
DLog(#"Great Success!");
}
}
}
Hope this works for you!
I'm making an application with database. For modal I use FMDatabase classes. Now I have to insert some new data in two tables:
-project(id_proj primary key autoince unique, name, baseImage)
-imagesforpro(id_img primkey autoincr unique, smallimage, largeimage, id_proj)
I have a method -(void)addProject:(Project*)proj for which I pass a Project object.
Then I have to insert projectName and baseImage into project table and then extract the id_proj of the record I've just inserted and insert small and larges images into imagesforpro table.How to remeber the id_proj(which is autoincrement) for the last inserted record? Moreover, how to use INSERT? DO I use it properly? how to put NSData into the database properly? thanks. Here is what I have so far:
-(void)addProject:(Project*)proj
{
[db open];
NSData *bmdata=UIImagePNGRepresentation(proj.baseImage);
if (proj==nil) {
NSLog(#"Dead Object");
}
NSLog(#" NEW name is %#", proj.projectName);
//DOESN'T WORK
//id_proj is autoincrement field that's why I just pass name and base image
[db executeUpdate:#"INSERT INTO project VALUES (?,?)", [proj.projectName UTF8String], bmdata];
//sqlite3_last_insert_rowid()
[db close];
sqlite3 *dataB;
int status=sqlite3_open([[self dbPath] UTF8String], &dataB);
if(status!=SQLITE_OK)
{
NSLog(#"Error");
//exit(1);
}
//RETURNS 0(
int lastindex=sqlite3_last_insert_rowid(dataB);
NSLog(#"Last inserted index %i", lastindex);
sqlite3_close(dataB);
[db open];
//Add images for the project (another table)
for(int i=0; i<[proj.largeImages count]; i++)
{
UIImage *smallImg=[proj.smallImages objectAtIndex:i];
UIImage *bigImg=[proj.largeImages objectAtIndex:i];
NSData *sidata=UIImagePNGRepresentation(smallImg);
NSData *bidata=UIImagePNGRepresentation(bigImg);
// Here I have to insert an image and id of the project it refers too
//How do I find out the last inserted id_proj?
[db executeUpdate:#"INSERT INTO imagesforpro VALUES (?,?,?)", sidata, bidata, lastindex];
}
[db close];
}