query sqlite3 database multiple tables - iphone

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.

Related

Performance issue in iOS-5 for Sqlite

I am facing one problem while using sqlite in iOS 5. I am fetching records from two tables: one in Recipe & other in Ingredients from one Menu.db
From Recipe table I get all record and one recipeid on that basis I fetch records from ingredients table. It takes no time to fetch record when run on iOS 4.2 but when I run on iOS 5 it takes time to fetch the records. See the following code:
NSString *query = [NSString stringWithFormat:#"select id from Recipes"];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
rcp.recipeID = sqlite3_column_int(selectstmt, 0);
NSString *sql = [NSString stringWithFormat:#"select Name from Ingredients where recipeId = %d",rcp.recipeID];
sqlite3_stmt *stmt2;
if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
while(sqlite3_step(stmt2) == SQLITE_ROW) {}
}
}
}
Why is this issue coming in iOS 5.0, the same code runs fine on iOS 4.0, 4.2?
I know, code I have written is right,I want to know the exact reason behind this Performance issue in iOS 5.0 for Sqlite bcoz my app is totally build around database.
Try with using two different functions
After you finish with complete execution of your first query, start with second query.
For example :-
NSString *query = [NSString stringWithFormat:#"select id from Recipes"];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
rcp.recipeID = sqlite3_column_int(selectstmt, 0);
}
}
and then call
NSString *sql = [NSString stringWithFormat:#"select Name from Ingredients where recipeId = %d",rcp.recipeID];
sqlite3_stmt *stmt2;
if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
while(sqlite3_step(stmt2) == SQLITE_ROW) {}
Hope this helps to solve your issue.
I think you linked against libsqlite3.dylib. You should link the libsqlite3.0.dylib library instead.
If you want contever your .db to .sqlite
open your .db file select the table File-> Export--> Table from CSV (save your file with .csv format)
(like this way you select all table)
then open .sqlite file
File-> Import--> Table from CSV
after your .csv file choose a dialog box appear
in that Extract field names from first line must tick marked
now your sqlite file is ready.
put this file into your project
then set your .sqlite/.db file path
now set your select query as like this
#import <sqlite3.h>
-(void)SelectSqlData:(NSString *)SearchString
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"yourfileName.sqlite"];
sqlite3_stmt *compiledStatement;
sqlite3 *database;
if(sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
const char *sqlStatement;
sqlStatement = "select c.field1,c.field2,c.field3,c.field4 from YourTableName1 as c, YourTableName2 as b where b.Artist_Id = ?";
sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL);
//printf("\nError===%s",sqlite3_errmsg(database));
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
sqlite3_bind_text(compiledStatement,1,[SearchString UTF8String] , -1,SQLITE_STATIC);
while(sqlite3_step(compiledStatement) == SQLITE_ROW )
{
NSString *str_field1=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 0)];
NSString *str_field2=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
NSString *str_field3=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 2)];
NSString *str_field4=[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 3)];
// add str_field into array
}
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
This probably isn't the answer you're looking for, but here's a small tip to improve performance.
NSString *query = [NSString stringWithFormat:#"select id from Recipes"];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
rcp.recipeID = sqlite3_column_int(selectstmt, 0);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//NSString *sql = [NSString stringWithFormat:#"select Name from Ingredients where recipeId = %d",rcp.recipeID];//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
sqlite3_stmt *stmt2;
if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
while(sqlite3_step(stmt2) == SQLITE_ROW) {}
}
}
}
Every iteration of the while loop, you create a new NSString object (NSString *sql = ...), so maybe you should instead do this:
NSString *query = [NSString stringWithFormat:#"select id from Recipes"];
NSString *sql = [NSString stringWithFormat:#"select Name from Ingredients where recipeId = %d",rcp.recipeID];
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &selectstmt, NULL) == SQLITE_OK) {
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
rcp.recipeID = sqlite3_column_int(selectstmt, 0);
sqlite3_stmt *stmt2;
if(sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt2, NULL) == SQLITE_OK) {
while(sqlite3_step(stmt2) == SQLITE_ROW) {}
}
}
}
Hope this helps a bit!
The function GetListBySQL is optimized and iOS versions independent. May it will help you out.
-(NSMutableArray*)GetListBySQL:(NSString*)SQL
{
NSMutableArray* Array;
Array=[[NSMutableArray alloc]init];
NSStringEncoding enc = [NSString defaultCStringEncoding];
sqlite3_stmt *select_statement=nil;
if (sqlite3_prepare_v2(database, [SQL UTF8String], -1, &select_statement, NULL) != SQLITE_OK) {
NSString *errString = [NSString stringWithFormat:#"%#", [#"Fail" stringByReplacingOccurrencesOfString:#"#" withString:[NSString stringWithCString:sqlite3_errmsg(database) encoding:enc] ]];
NSAssert1(0, #"%#", errString);
}
int columncount=sqlite3_column_count(select_statement);
NSMutableDictionary* dic;
while (sqlite3_step(select_statement) == SQLITE_ROW)
{
dic=[[NSMutableDictionary alloc]init];
for(int j=0;j<columncount;j++)
{
if(sqlite3_column_text(select_statement, j)!=nil)
[dic setObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(select_statement, j)] forKey:[NSString stringWithUTF8String:(char *)sqlite3_column_name(select_statement,j)]];
else
[dic setObject:#"" forKey:[NSString stringWithUTF8String:(char *)sqlite3_column_name(select_statement,j)]];
}
[Array addObject:dic];
[dic release];
}
sqlite3_finalize(select_statement);
NSMutableArray *arr = [[NSMutableArray alloc] initWithArray: Array];
[Array release];
return arr;
}
Another alternative is to change SQLite to a Key/Value database like LevelDB (from google) or TokyoCabinet. I'm using LevelDB for two project right now and is working really good, and I used TokyoCabinet in the past also, the problem with TokyoCabinet is that is LGPL, so I'm not sure if is fully compatible with the iOS environment, but anyway I had several Apps in the appstore using Tokyo Cabinet (don't tell Apple).
For using both of them you will need a wrapper (Or maybe you can develop your own). This is a quick comparison and the available wrappers:
LevelDB: It seems to be one of the fastest out there (if not the fastest, take a look at their benchmarks). And as wrapper I'm currently using NULevelDB, if you have any problems adding it to your project let me know (I had some).
TokyoCabinet: It seems to be no so fast as LevelDB (I haven't run tests, I dropped it because of the license problems), but in the official page they recommend using their new library called KyotoCabinet that I haven't tested yet but is supposed to be faster. The wrapper I used was made by the amazing Aaron Hillegass, and it is called BNRPersistence.
As a recommendation, give a try to LevelDB, there is a bigger community behind, and the wrapper (NULevelDB) is simple and friendly.
Good luck!

Multiple queries in SQLite for iOS

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.

iOS SQLite Sum and retrieve data

I have a SQLite database that I am creating in my iOS application. A series of numbers are being stored in this database. I want to sum the entire column, and return the data to be displayed within the application.
Everything writing to the DB is working properly, but I am stuck trying to return the summed data. Any help would be greatly appreciated.
-(void) dataReturn: (NSString *) tableNamed{
NSString *myData = [NSString stringWithFormat:#"SELECT SUM(column1) AS data1 FROM myDB", tableNamed];
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(db, [myData UTF8String], -1, &statement, nil) ==SQLITE_OK){
while (sqlite3_step(statement) == SQLITE_ROW){
int *field2 = (int *) sqlite3_column_int(statement, 1);
NSString *myString =[[NSString alloc] initWithFormat:#"%#", field2];
}
}
}
Hello Nathan making a call as
[self dataReturn:#"Bill"];
to function
-(void)dataReturn:(NSString *)tableName{
sqlite3 *database;
sqlite3_stmt *statement;
NSString *queryString = [NSString stringWithFormat:#"SELECT SUM(price) AS TOTAL FROM %#", tableName];
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
if(sqlite3_prepare_v2(database, [queryString UTF8String], -1, &statement, nil) == SQLITE_OK){
while (sqlite3_step(statement) == SQLITE_ROW) {
int field1 = sqlite3_column_int(statement, 0);
NSLog(#"The sum is %d ", field1);
}
}
}
}
will Fetch you desired data. The schema for Table "Bill" is "CREATE TABLE Bill (price double,quantity INTEGER)". The result fetched will have columns indexed from "0" so we pass 0 for first column. Hope you can take some hint from it. Cheers!!
int field1 = sqlite3_column_int(statement, 0);
From the sqlite3_column_int docs:
The leftmost column of the result set has the index 0.
Additionally, that function returns an int, not an int*. Try:
int field2 = sqlite3_column_int(statement, 0);

iPhone SQLITE Select statement with variables

I am creating an iOS app that reads data from a single SQLITE table using variables. I don't have a problem running my select statement when all variables are populated, but I want to eventually have a large amount of variables and allow the users to skip ones that they don't see as meaningful to them. In other words, how can I make this work even when variables are null or 0 such as ignoring that part of the select statement, but continuing on? I have tried to use IF statements or CASE statements, but then I get the undeclared error. I could repeat the entire getInitialDataToDisplay with IFs, but there has to be an easier way.
+ (void) getInitialDataToDisplay:(NSString *)dbPath{
int addOne = [[NSUserDefaults standardUserDefaults] integerForKey: #"criterion1key"];
int addTwo = [[NSUserDefaults standardUserDefaults] integerForKey: #"criterion2key"];
int addThree = [[NSUserDefaults standardUserDefaults] integerForKey: #"criterion3key"];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
NSString *querystring= [NSString stringWithFormat:#"select * from animalswhere description > %i and description < %i and cash >= %i",addOne, addTwo, addThree]; //WORKS FINE IF ALL VARIABLES HAVE VALUES, BUT DOES NOTHING IF VARIABLES ARE EMPTY
const char *sql = [querystring UTF8String];
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);
Animal *animal = [[Animal alloc] initWithPrimaryKey:primaryKey];
animal.name = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 3)];
animal.description = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 4)];
animal.imageURL = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 5)];
animal.cash = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 32)];
[appDelegate.animals addObject:animal];
[animal release];
}
}
} else sqlite3_close(database);
}
Sounds like what you need is to use marker variables/values, and a little boolean logic:
SELECT valuea, valueb, valuec
FROM table
WHERE valuea = %inputParmA
AND (%inputParmB = 0 OR valueb = %inputParmB)
This will have the effect of selecting all rows where valuea matches the passed-in value (of inputParmA), and, if inputParmB is non-zero, valueb matches inputParmB.
You'll have to adapt this for your needs, but it's a quick-and-dirty alternative to using dynamic sql, if this isn't available (or difficult to generate).
Not completely sure about performance, but I have a query that has around a dozen of these, operating over a multi-million row database, and returns within a minute (hundreds of results).

iphone xcode sqlite3_open remote host

i try to get a connection my server, with the sqlite3_open command!
my question...is it possible to that? i got the following code...
// Get the path to the documents directory and append the databaseName
databaseName = #"AnimalDatabase.sql";
NSString *serverpath = #"http://localhost/app/";
databasePath = [serverpath stringByAppendingPathComponent:databaseName];
and then this here
-(void) readAnimalsFromDatabase {
// Setup the database object
sqlite3 *database;
// Init the animals Array
animals = [[NSMutableArray alloc] init];
// 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];
// Add the animal object to the animals Array
[animals addObject:animal];
[animal release];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
any suggestion??
Where you get the idea that SQLite can open database from URL??
It can open files only (or, create temporary db in memory).
The answer is just NO, there are lot of mistakes in your code anyway. For example:
databaseName = #"AnimalDatabase.sql";
Where did you get this. iPhone is working with sqlite database, it has nothing in common with sql files:)