I have an SQLite database in my iOS application. I'm trying to use the SQL function MAX()
- (void) getMaxTime {
if (sqlite3_open([databasePath UTF8String], &timingsDatabase) == SQLITE_OK)
{
NSString *maxTimeStatement = [NSString stringWithFormat:#" SELECT max(NUMBERS) FROM TIMINGS"];
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(timingsDatabase, [maxTimeStatement UTF8String], -1, &statement, nil) == SQLITE_OK){
while (sqlite3_step(statement) == SQLITE_ROW) {
massimo = sqlite3_column_int(statement, 0);
NSLog(#"The maximun time is %d seconds ", massimo);
}
sqlite3_finalize(statement);
sqlite3_close(timingsDatabase);
}
}
All the numbers in the column NUMBERS have 6 digits after the coma.
Everything works fine if all the numbers are > 10 or all the numbers are <10.
But (example) when I try to get the max among:
1.339670
2.955537
11.355558
It prints: "The maximun time is 2 seconds" (instead of 11)! I'm struggling to understand why.
*EDIT
This is how my Database was created:
-(void)createDatabase
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
databasePath = [[NSString alloc]initWithString:[documentsDirectory stringByAppendingPathComponent:#"timings.db"]];
if ([[NSFileManager defaultManager] fileExistsAtPath:databasePath] == FALSE)
{
if (sqlite3_open([databasePath UTF8String], &timingsDatabase) == SQLITE_OK)
{
const char *sqlStatement = "CREATE TABLE IF NOT EXISTS TIMINGS (ID INTEGER PRIMARY KEY AUTOINCREMENT, TIMESTAMP TEXT, TIMING TEXT, NUMBERS TEXT)";
char *error;
sqlite3_exec(timingsDatabase, sqlStatement, NULL, NULL, &error);
sqlite3_close(timingsDatabase);
}
}
}
And this is how timings are stored:
-(void)storeTiming
{
if (sqlite3_open([databasePath UTF8String], &timingsDatabase) == SQLITE_OK)
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss.SSS"];
NSString *insertStatement = [NSString stringWithFormat:#"INSERT INTO TIMINGS (TIMESTAMP, TIMING, NUMBERS) VALUES (\"%#\", \"%#\", \"%f\")", [dateFormatter stringFromDate:startDate], stopWatchLabel.text , intervallo] ;
char *error;
sqlite3_exec(timingsDatabase, [insertStatement UTF8String], NULL, NULL, &error);
sqlite3_close(timingsDatabase);
}
}
Seems that column's data type is string... You should change it to float to use MAX() function.
Related
I have create sqlite db Table as below
NSString * sqlStmt =#"CREATE TABLE IF NOT EXISTS SONGS (ID INTEGER PRIMARY KEY AUTOINCREMENT, SONGNAME TEXT UNIQUE NOT NULL,ALBUMNAME TEXT ,ARTIST TEXT ,SIZE FLOAT ,IMAGE BLOB)";
when i am trying to get data from this table getting nothing
NSString *sqlQuery = [NSString stringWithFormat:
#"SELECT ARTIST ,IMAGE FROM SONGS WHERE SONGNAME=kannulada"];
but iam getting data when iam trying to get data by id.
NSString *sqlQuery = [NSString stringWithFormat:
#"SELECT ARTIST ,IMAGE FROM SONGS WHERE id=1"];
when i debugging it not excuting prepare loop
-(NSMutableArray*)retrieveDataArrayWHICHISequql
{
[dataArray removeAllObjects];
sqlite3_stmt *statement;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &_SQliteDB) == SQLITE_OK)
{
const char * selectQuery =[sqlQuery UTF8String];
if (sqlite3_prepare_v2(_SQliteDB, selectQuery, -1, &statement, NULL)== SQLITE_OK)
{
while (sqlite3_step(statement)== SQLITE_ROW)
{
NSString * addressStr = [[NSString alloc]initWithUTF8String:(const char*)sqlite3_column_text(statement, 0)];
NSString * phNumStr = [[NSString alloc]initWithUTF8String:(const char*)sqlite3_column_text(statement, 1)];
// NSString * nameStr = [[NSString alloc]initWithUTF8String:(const char*)sqlite3_column_text(statement, 2)];
NSMutableDictionary * dataDict = [[NSMutableDictionary alloc]init];
[dataDict setObject:addressStr forKey:#"getaddress"];
[dataDict setObject:phNumStr forKey:#"getphone"];
// [dataDict setObject:nameStr forKey:#"getname"];
[dataArray addObject:dataDict];
}
sqlite3_finalize(statement);
sqlite3_close(_SQliteDB);
}
}
return dataArray;
NSString *sqlQuery = [NSString
stringWithFormat:
#"SELECT ARTIST ,IMAGE FROM SONGS WHERE SONGNAME='kannulada'"]
;
kannulada is string , so you must use single quote
The problem is that if you have an error in sqlite3_prepare_v2 (it didn't return SQLITE_OK). So, if that happens, you should look at what sqlite3_errmsg returned, and it will tell you precisely why it failed. If you don't look at the meaningful error message that SQLite returns, you're flying blind.
if (sqlite3_prepare_v2(_SQliteDB, selectQuery, -1, &statement, NULL)== SQLITE_OK)
{
...
} else {
NSLog(#"SQL Error: %s", sqlite3_errmsg(_SQliteDB));
}
Regarding why it's failing, it's because you have to either quote the string literal, 'kannulada', or, better, use a SQL placeholder, ?, and then use sqlite3_bind_text.
I want to update a row in my iPhone app. Below is the code. It shows in my log that query is executed and row has been updated, but when I excess that table, it seems to be empty. And also it always updates a row even if I am trying to enter a row with new id, it should insert instead of updating in this case.
//create local database
NSString *docsDir = NULL;
NSArray *dirPaths = NULL;
sqlite3 *localDB = NULL;
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the database file
NSString *databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"localscentsy1.db"]];
NSLog(#"%#",databasePath);
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement = NULL;
if (sqlite3_open(dbpath, &localDB) == SQLITE_OK)
{
NSString *updateSQL = [NSString stringWithFormat:#"UPDATE Ratings set rating = '%d' WHERE perfume_id = ?",
(int)rating];
const char *update_stmt = [updateSQL UTF8String];
if(sqlite3_prepare_v2(localDB, update_stmt, -1, &statement, NULL ) ==SQLITE_OK){
sqlite3_bind_int(statement, 1, tappedItem.perfumeId);
}
char* errmsg;
sqlite3_exec(localDB, "COMMIT", NULL, NULL, &errmsg);
if(SQLITE_DONE != sqlite3_step(statement)){
NSLog(#"Error while updating. %s", sqlite3_errmsg(localDB));
NSLog(#"query failed: %s", sqlite3_errmsg(localDB));
NSLog(#"%#",#"update unsuccessfull");
NSLog(#"New data, Insert Please");
sqlite3_stmt *statement2 = NULL;
NSString *insertSQL = [NSString stringWithFormat:
#"INSERT INTO Ratings (perfume_id,rating) VALUES (\"%d\",\"%d\")",
tappedItem.perfumeId,
(int)rating];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(localDB, insert_stmt, -1, &statement2, NULL);
if (sqlite3_step(statement2) == SQLITE_DONE)
{
NSLog(#"New data, Inserted");
}
sqlite3_finalize(statement2);
}
else{
NSLog(#"%#",#"update successfull");
sqlite3_finalize(statement);
}
sqlite3_close(localDB);
}
Any help will be appreciated. I am new at ios development and this code is taking so much of my time. Here is my log which always says "update successful"
CyberGenies/Library/Application Support/iPhone Simulator/7.0/Applications/24D2FEE2- DBE6-441A-AC79-1F2D57F96C88/Documents/localscentsy1.db
2014-07-27 01:03:26.612 Scentsy Squirrel[19008:a0b] update successfull
Have you seen if the db really has the structure? Some times when you start doing queries in an empty db (I mean not even tables) the query itself build the structure you are giving it on the query. You can check by compiling on the simulator and look for the db in the Mac finder.
On the first implemetation file, all steps
- (void) readCitiesFromDatabase {
databaseName = #"123.sqlite";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
// Setup the database object
sqlite3 *database;
// Setup the SQL Statement and compile it for faster access
NSString *querySQL = [NSString stringWithFormat:#"SELECT City_Name, City_Picture, City_Id FROM CITIES, COUNTRIES WHERE CITIES.Country_Id=COUNTRIES.Country_Id AND COUNTRIES.Country_Id = '%#'", self.countryID];
const char *sqlStatement = [querySQL UTF8String];
sqlite3_stmt *compiledStatement;
// Init the countries Array
self.cities = [[NSMutableArray alloc] init];
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSLog(#"success");
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
NSLog(#"success");
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
NSLog(#"success");
// Read the data from the result row
char *temp_cName = (char *)sqlite3_column_text(compiledStatement, 0);
NSString *cName = temp_cName == NULL ? nil : [[NSString alloc] initWithUTF8String:temp_cName];
char *temp_cPicture = (char *)sqlite3_column_text(compiledStatement, 1);
NSString *cPicture = temp_cPicture == NULL ? nil : [[NSString alloc] initWithUTF8String:temp_cPicture];
char *temp_cId = (char *)sqlite3_column_text(compiledStatement, 2);
NSString *cId = temp_cId == NULL ? nil : [[NSString alloc] initWithUTF8String:temp_cPicture];
NSMutableDictionary *city = [[NSMutableDictionary alloc] init];
[city setObject:cName forKey:#"City_Name"];
[city setObject:cPicture forKey:#"City_Picture"];
[city setObject:cId forKey:#"City_Id"];
[self.cities addObject:city];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
Now I got cities list. When I select a city, I could not see city info.
This is from second implementation.
- (void) readCityInfoFromDatabase:(NSString *)querySQL
{
databaseName = #"123.sqlite";
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
// Setup the database object
sqlite3 *database;
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = [querySQL UTF8String];
sqlite3_stmt *compiledStatement;
// Open the database from the users filessytem
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
NSLog(#"success");
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
NSLog(#"success");
if (sqlite3_step(compiledStatement) == SQLITE_ROW)
{
NSLog(#"success");
char *temp_content = (char *)sqlite3_column_text(compiledStatement, 0);
[self.cityTextView setText:temp_content == NULL ? nil : [[NSString alloc] initWithUTF8String:temp_content]];
}
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
The first two steps are success but,
if (sqlite3_step(compiledStatement) == SQLITE_ROW)
{
NSLog(#"success");
I couldn't see success on this step.
Please help.
You really should be saving the result of sqlite3_step() and checking to see if it is:
SQLITE_ROW = retrieved row of data
SQLITE_DONE = no (more) data to retrieve
Other = some error occurred
On the basis of what you shared, though, there is a good chance that you simply received SQLITE_DONE because there was nothing found. You could get that if there was a mistake in the SQL (e.g. bad join, incorrect WHERE clause, etc.). It's hard to say without seeing the SQL used by readCityInfoFromDatabase and some representative data from the database.
By the way, not only for sqlite3_step(), but rather for all of your sqlite3_xxx() calls, if you receive an error, you should be logging sqlite3_errmsg(). Right now, if there was an error, you'd have to guess what the source of the problem was.
I have a table called Budgets that stores its settings, values, and a purchases nsarray full of the object purchases.
I am not sure what I am doing wrong in my method right here, but it will not update the database on an existing entry. It saves a new budget just fine.
- (BOOL) saveBudget:(Budget *)budget
{
BOOL success = false;
sqlite3_stmt *statement = NULL;
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
if (budget.budgetID > 0) {
NSLog(#"Existing data, Update Please");
NSString *updateSQL = [NSString stringWithFormat:#"UPDATE BUDGETS set name = '%#', budget = '%#', customdate = '%d', customday = '%d', rolloverp = '%d', rollovern = '%d', purchases = ?8 WHERE id = ?", budget.name, budget.budget, budget.customDate, budget.customDay, budget.rolloverP, budget.rolloverN];
const char *update_stmt = [updateSQL UTF8String];
sqlite3_prepare_v2(database, update_stmt, -1, &statement, NULL);
sqlite3_bind_int(statement, 1, budget.budgetID);
sqlite3_bind_blob(statement, 8, [budget.purchases bytes], [budget.purchases length], SQLITE_TRANSIENT);
if (sqlite3_step(statement) == SQLITE_DONE)
{
success = true;
}
}
else
{
NSLog(#"New data, Insert Please");
NSString *insertSQL = [NSString stringWithFormat:#"INSERT INTO BUDGETS (name, budget, customdate, customday, rolloverp, rollovern, purchases) VALUES (\"%#\", \"%#\", \"%d\", \"%d\", \"%d\", \"%d\", ?8)", budget.name, budget.budget, budget.customDate, budget.customDay, budget.rolloverP, budget.rolloverN];
const char *insert_stmt = [insertSQL UTF8String];
sqlite3_prepare_v2(database, insert_stmt, -1, &statement, NULL);
sqlite3_bind_blob(statement, 8, [budget.purchases bytes], [budget.purchases length], SQLITE_TRANSIENT);
if (sqlite3_step(statement) == SQLITE_DONE)
{
success = true;
}
}
sqlite3_finalize(statement);
sqlite3_close(database);
NSLog(#"Save Success");
}
return success;
}
I am assuming that the query is incorrect, but I may be wrong.
EDIT:
Here is how I am adding the database. Again, new entries are added and retrieved from the database just fine. Old entries are retrieved but will not update.
- (void) initDatabase
{
NSString *docsDir;
NSArray *dirPaths;
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
databasePath = [[NSString alloc] initWithString:
[docsDir stringByAppendingPathComponent:#"budget.db"]];
NSFileManager *filemgr = [NSFileManager defaultManager];
if([filemgr fileExistsAtPath:databasePath] == NO)
{
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &database) == SQLITE_OK)
{
NSString sql_stmt = #"CREATE TABLE IF NOT EXISTS BUDGETS (";
sql_stmt = [sql_stmt stringByAppendingString:#"id INTEGER PRIMARY KEY AUTOINCREMENT, "];
sql_stmt = [sql_stmt stringByAppendingString:#"name TEXT, "];
sql_stmt = [sql_stmt stringByAppendingString:#"budget DOUBLE, "];
sql_stmt = [sql_stmt stringByAppendingString:#"customdate INTEGER, "];
sql_stmt = [sql_stmt stringByAppendingString:#"customday INTEGER, "];
sql_stmt = [sql_stmt stringByAppendingString:#"rolloverp INTEGER, "];
sql_stmt = [sql_stmt stringByAppendingString:#"rollovern INTEGER, "];
sql_stmt = [sql_stmt stringByAppendingString:#"purchases BLOB)"];
if (sqlite3_exec(database, [sql_stmt UTF8String], NULL, NULL, &errMsg) != SQLITE_OK)
{
NSLog(#"Failed to create table BUDGETS");
}
else
{
NSLog(#"BUDGETS table created successfully");
}
sqlite3_close(database);
}
else
{
NSLog(#"Failed to open/create database");
}
}
}
ANOTHER QUESTION:
I also have a question on whether I am saving the purchases NSArray correctly or not. I assumed that this would work.
Make sure the Location your pointing to the SQlite data base is right.And before executing the query make sure the table is in open state and ready to the operation. Refer this code:
sqlite3_stmt *statement;
sqlite3 *cPDB;
NSString *docsDir;
NSArray *dirPaths;
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Data base path
databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"cPDB.db"]];
const char *dbpath = [databasePath UTF8String];
if (sqlite3_open(dbpath, &cPDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat: #"UPDATE USER_SESSION_INFO SET \"%#\" = \"%#\", synState = 'false' WHERE sessionName=\"%#\"",categoryTy,state,sessionName];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(cPDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
if((sqlite3_step(statement)) != SQLITE_DONE)
NSLog(#"Not Updated");
else
{
NSLog(#"Succesfully updated row !");
return YES;
}
}
}
Found the problem. There were no single quotes around ?8 in the update statement. Well, its always the small problems. Thanks everyone!
I want to get data of my group by query and load them into textfields.
here is my table
integer ID
Name text
Duration Real
Time Real
there are few names in in Name column
I tried calling method for each name , but it caused insert failure.
I want receive this query and load the result on specific Label: i dont know how can get the response
SELECT Name,SUM (time) FROM cost GROUP BY Name;
there is my current code
NSString *docsDir;
NSArray *dirPaths;
double totaltime = 1.0;
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
// Build the path to the database file
databasePath = [[NSString alloc]
initWithString: [docsDir stringByAppendingPathComponent:
#"Database.db"]];
NSString *queryString=#"SELECT SUM(DURATION) AS TOTAL FROM RECORDS";
sqlite3_stmt *statement;
if(sqlite3_open([databasePath UTF8String], &myDatabase) == SQLITE_OK) {
if(sqlite3_prepare_v2(myDatabase, [queryString UTF8String], -1, &statement, nil) == SQLITE_OK){
while (sqlite3_step(statement) == SQLITE_ROW) {
totaltime = sqlite3_column_double(statement, 0);
NSLog(#"The sum is %f ", totaltime);
}
}
}
integer_t intotal=totaltime;
NSString* total=[NSString stringWithFormat:#"%d min",intotal];
totaltimefield.text=total;
This is all covered pretty nicely in the documentation, but, in short, once you update your SQL to have two columns:
const unsigned char* name = sqlite3_column_text(statement, 0);
double totaltime = sqlite3_column_double(statement, 1);
NSLog(#"The sum for %s is %f ", name, totaltime);
The second parameter int the sqlite3_column_x functions is the column number.