Multiple queries in SQLite for iOS - iphone

Need help here.
i managed to get the drinkName and categories working. http://dl.dropbox.com/u/418769/2.png
but i need to Distinct the categories and get a count value of the SQL database.
something like this http://dl.dropbox.com/u/418769/2.png and then this http://dl.dropbox.com/u/418769/3.png
how would i need to do to get it done ?
i need to run SELECT drinkID,drinkName from drinks,
SELECT DISTINCT categories from drinks &
count each categoies's row.. can it be done ?
this is my database looks like..http://dl.dropbox.com/u/418769/4.png
i'm following this http://mybankofknowledge.wordpress.com/2010/12/01/accessing-sqlite-from-iphone-table-view/
ok it kind of work.....i on this error
* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[NSMutableArray objectAtIndex:]: index 5 beyond bounds [0 .. 4]'
(void) getInitialDataToDisplay:(NSString *)dbPath {
DrinkTabsAndNavAppDelegate *appDelegate = (DrinkTabsAndNavAppDelegate *)[[UIApplication sharedApplication] delegate];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "SELECT drinkID, drinkName FROM drinks";
//const char *sql = "SELECT drinkName, categories FROM drinks";
sqlite3_stmt *selectstmt;
if (sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW)
{
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Drink *drinkObj = [[Drink alloc] initWithPrimaryKey:primaryKey];
drinkObj.drinkName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt,1)];
//drinkObj.categories = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 6)];
drinkObj.isDirty = NO;
[appDelegate.drinksArray addObject:drinkObj];
[drinkObj release];
}
}
} else sqlite3_close(database); //close db to release all memory
}
(void) getCategory:(NSString *)dbPath {
DrinkTabsAndNavAppDelegate *appDelegate = (DrinkTabsAndNavAppDelegate *)[[UIApplication sharedApplication] delegate];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "SELECT DISTINCT category FROM drinks";
//const char *sql = "SELECT drinkName, categories FROM drinks";
sqlite3_stmt *selectstmt;
if (sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW)
{
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Drink *catObj = [[Drink alloc] initWithPrimaryKey:primaryKey];
catObj.category = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 0)];
//drinkObj.categories = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 6)];
NSLog(#"run here");
catObj.isDirty = NO;
[appDelegate.categoryArray addObject:catObj];
[catObj release];
}
}
} else sqlite3_close(database); //close db to release all memory
}

If you want a count of the drinks in each category:
select category, count(drinkid) as DrinksInThisCategory
from drinks
group by category
But a properly normalized database would have a separate CATEGORIES table and you'd have a categoryid in your DRINKS table.

There are two ways i can come up with, one is querying your db: SELECT drinkID FROM drinks where categories='catX' once per category, then count rows of each. I think this could be accomplished by using some SQLite code to return all the counts with a single query.
The other way is using you appDelegate.drinksArray. First order your query by categories, then you could use a predicate almost same as the one above and using FOR drink IN drinksArray (pseudo code here...) to count how many drinks have each category.
Performance wise, i think the best way is using sql to query both, drinks and count of each category, may be in two different queries o best in one.

I would probably go with an approach that used an NSMutableDictionary instead of and array as the main datastructure.
-(void) addDrink:(Drink) drink
{
//_drinks has been initialised earlier and is of type NSMutableDictionary
NSMutableArray categoryDrinks = [_drinks objectForKey:drink.categories];
if (categoryDrinks == nil)
{
categoryDrinks = [[[NSMutableArray alloc] init] autorelease];
[_drinks setObject:categoryDrinks forKey:drink.categories];
}
[categoryDrinks addObject:drink];
}
This mimics the structure that you show in your images, the number of items in each category can now be found by calling [[_drinks objectForKey:categoryName] count]. Look at the apple documentation for NSMutableDictionary and NSMutableArray
If you just want the numbers there is the SQL GROUP BY expression you can get to the category counts doing something like this
SELECT CategoryColName, COUNT(CategoryColName) FROM TableName GROUP BY CategoryColName
This will give you pairs with the name of your category and the count
One small advice, in your Drinks object you use the property categories. I always try to keep the plurality of the name in sync with the type of data. This means I only use the plural for something if it is a collection of sorts. Otherwise I use the singular, this can be extended to SQL columns, while the column contains the categories of all the drinks. It is still only one category per drink. You did name the other columns using the singular.

Related

Iphone:Loading Limited Records In table View from Sqlite3 database

I have more than 100 records in a database of a particular user. Now I want to display only the first 30 from it, and when a "more" button is pressed, 30 more records will appear below them. How can I do it in an iPhone application?
You can use SQL Query to achieve your goal
-(NSMutableArray *)SelectResult:(int)offset
{
NSMutableArray *arrdata;
arrdata=[[NSMutableArray alloc]init];
sqlite3 *database;
NSString *databasePath = [self getDBPath];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSString *sqlQuery=[NSString stringWithFormat:#"Select * from TableName where RecordId > %d and RecordId <=%d",offset,offset+30];
sqlite3_stmt *compiledStatement;
compiledStatement=nil;
if(sqlite3_prepare_v2(database, [sqlQuery UTF8String], -1, &compiledStatement, NULL) == SQLITE_OK)
{
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
if ((char *)sqlite3_column_text(compiledStatement,0)!=0)
{
NSMutableDictionary *temData = [[NSMutableDictionary alloc] init];
int pKey = sqlite3_column_int(compiledStatement,0);
[temData setValue:[NSString stringWithFormat:#"%d",pKey] forKey:#"RecordId"];
[arrdata addObject:temData];
}
}
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
return arrdata;
}
Please call this method and pass offset as integer it will give you 30 recored from the offset
like offset = 0 then 1 to 30
offset = 30 then 31 to 60 etc....
Hope it will help you
Happy coding
Just use a NSMutableArray to hold you data. In your table view datasource make sure it displays an extra cell (with the "More" message) if there are any more records to display.
In didSelectRowAtIndexPath you can then modify the table view's data array by adding additional records from the database.
You can generate your SQL query dynamically based on the size of your data array.

Selecting data from data base form two tables in iphone app

i want to select data from the table but those data which has same question id in the questions table and answers table.
Now it selects all the data from answers the code is shown below
+ (void) getAnswers:(NSString*)dbPath{
CereniaAppDelegate *appDelegate = (CereniaAppDelegate *)[[UIApplication sharedApplication] delegate];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
const char *sql = "select *from answers";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Answers *coffeeObj = [[Answers alloc] initWithPrimaryKey:primaryKey];
coffeeObj.answer_text = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 3)];
NSString*testString=coffeeObj.answer_text;
NSLog(testString);
[appDelegate.answerArray addObject:coffeeObj];
int mycount=[appDelegate.answerArray count];
NSLog(#"This is int of latest %d",mycount);
[coffeeObj release];
}
}
}
else
sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory.
}
I suggest avoiding c level code for calling a SQLite DB - try a simple wrapper over SQLite - see https://github.com/JohnGoodstadt/EasySQLite
This will allow:
DataTable* result = [_db ExecuteQuery:#"SELECT * FROM answers"];
It feels like you need to use a LEFT JOIN statement to answer your actual query - if you add info about your table definitions and what data you are trying to get out I can help more.

How can I query a sqlite database using objective c?

I'm trying to display food by type to a user. The different types of foods are different elements in a table (fruits, veggies, meats, etc). I have all the foods in one database using sqlite. How can I query this database using objective c so that I can display only the correct type of food the user selected in the next view's table?
We use FMDB in two apps. It works fine.
-(NSArray *)GetAllFoods
{
NSMutableArray *filesArray = [[NSMutableArray alloc] init];
// Open the database from the users filessytem
NSString *DBPath = [ClsCommonFunctions GetDatabasePath];
if ([self OpenDBWithPath:DBPath])
{
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "your query";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(filesDB, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
// Need to get data from database...
NSSstring *foodObj = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)]
[filesArray addObject:foodObj];
FileObj = nil;
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
sqlite3_close(filesDB);
}
return filesArray;

Problem getting a BOOL from a SQLite DB

I'm writing an app using a SQLite db to store information. My db table includes 5 fields: gameID (the primary key, int), gameName (Varchar), isLocked, isHidden, and isFavorite (all bools). I've set up the db using SQLite Manager in FireFox.
+ (void) getInitialDataToDisplay:(NSString *)dbPath {
FightingGamesGuideAppDelegate *appDelegate = (FightingGamesGuideAppDelegate *)[[UIApplication sharedApplication] delegate];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
//My second thought is that this line is incorrect. (see below)
const char *sql = "select gameID, gameName, isLocked, isHidden, isFavorite from GameTable";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Games *coffeeObj = [[Games alloc] initWithPrimaryKey:primaryKey];
//getGameName
coffeeObj.gameName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
//I think these three lines are where the problem is
coffeeObj.isLocked=(BOOL)sqlite3_column_int(selectstmt, 2);
coffeeObj.isHidden=(BOOL)sqlite3_column_int(selectstmt, 3);
coffeeObj.isFavorite=(BOOL)sqlite3_column_int(selectstmt, 4);
//This line is only to check if the data loaded properly
NSLog(#"%i, %i, %i",isLocked, isHidden, isFavorite);
[appDelegate.coffeeArray addObject:coffeeObj];
[coffeeObj release];
}
}
}
else{
sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory.
}
}
My question: Is this how to get a bool from a SQLite db? Is there a better way, such as using an int as a bool instead? If my code is all correct, do you have any other suggestion for how I can do this?
I know that the code aside from those three lines (and the following line) work properly. In the db, I've randomly assigned the three bools to YES or NO. I've also tried 1 and 0, to no effect.
If the lines are correct, my second thought is that line 9 is wrong. I modified it from "const ... ="select gameID, gameName from GameTable", and gameID and gameName still work properly.
My third thought is that I'm not updating the db correctly. I won't go into that, besides to ask if there's a specific "save" button/list item. If I update a gameName, that shows in my app, suggesting the db saves automatically.
In looking through the other questions, I've only found suggestions that CoreData is a better idea. However, if I'm on the right track, I only need these three lines to work, and I'm done (with this part).
Aside-I know that nothing is done with the bool variables besides printing them to the console. That's my next challenge.
Thank you for your time!
In addition to p.campbell's answer (+1), you could recall your BOOL value like this if you are storing YES/NO in your DB:
coffeeObj.isLocked=[[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)] isEqualToString:#"YES"];
coffeeObj.isHidden=[[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 3)] isEqualToString:#"YES"];
coffeeObj.isFavorite=[[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 4)] isEqualToString:#"YES"];
The SQLite datatypes page has the answer you're looking for:
SQLite does not have a separate Boolean storage class. Instead, Boolean values are stored as integers 0 (false) and 1 (true).

query sqlite3 database multiple tables

In the example below uses a single database table (Animals), which contains three columns (name, description, photo).
If my database contains two tables (1.animals, 2.Cities), each with its columns (name, description, photo), how do I ask for eg. "names Cities", which is positioned in column 1 table 2 .,in combination with "animal description",positioned in table 1 column 2,to make an object with these "values".
referncing this.....(char *)sqlite3_column_text(compiledStatement, 1)....
Thank you.
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "select * from animals";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSString *aName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *aDescription = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSString *aImageUrl = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
// Create a new animal object with the data from the database
Animal *animal = [[Animal alloc] initWithName:aName description:aDescription url:aImageUrl];
If the columns of both tables are name, description and photo, I don't see a relationship between them. Anyway, here's an SQL statement that does what you're asking for, but it doesn't make much sense:
SELECT c.name, a.description FROM Cities AS c, animals AS a;
If I have misunderstood your question let me know.