I have a database, and i do a S ELECT statement on SQL for 10 random rows.
It's for Iphone App, so Objective C.
How could i get back the information after the statement ?
...
const char *sql3 = [[NSString stringWithFormat:#"SELECT id FROM tabledesquestions ORDER BY RANDOM() LIMIT 10"] cStringUsingEncoding:NSUTF8StringEncoding];
sqlite3_stmt *sql1Statement;
if(sqlite3_prepare(database1, sql3, -1, &sql1Statement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement");
}
while (sqlite3_step(sql1Statement)==SQLITE_ROW) {
numeroqdonnee = sqlite3_column_int(sql1Statement, 0);
}
For now, i get back info just for the first row. How could i get back the info (id) for the others rows.
I would like something like that
numeroqdonnee2 =
numeroqdonnee3 =
numeroqdonnee4 =
...
Many thanks
You could add the results to some array:
const char *sql3 = [[NSString stringWithFormat:#"SELECT id FROM tabledesquestions ORDER BY RANDOM() LIMIT 10"] cStringUsingEncoding:NSUTF8StringEncoding];
NSMutableArray *results = [NSMutableArray array];
sqlite3_stmt *sql1Statement;
int returnCode;
if(sqlite3_prepare(database1, sql3, -1, &sql1Statement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(database1));
}
else
{
while ((returnCode = sqlite3_step(sql1Statement)) == SQLITE_ROW) {
numeroqdonnee = sqlite3_column_int(sql1Statement, 0);
[results addObject:#(numeroqdonnee)];
}
if (returnCode != SQLITE_DONE)
NSLog(#"Problem with step statement: %s", sqlite3_errmsg(database1));
sqlite3_finalize(sql1Statement);
}
// now do whatever you want with this results array
NSLog(#"results = %#", results);
// or
[results enumerateObjectsUsingBlock:^(NSNumber *obj, NSUInteger idx, BOOL *stop) {
NSLog(#"numeroqdonnee%d = %d", idx, [obj integerValue]);
}];
Or you could just log the results directly:
const char *sql3 = [[NSString stringWithFormat:#"SELECT id FROM tabledesquestions ORDER BY RANDOM() LIMIT 10"] cStringUsingEncoding:NSUTF8StringEncoding];
sqlite3_stmt *sql1Statement;
int returnCode;
if(sqlite3_prepare(database1, sql3, -1, &sql1Statement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(database1));
}
else
{
while ((returnCode = sqlite3_step(sql1Statement)) == SQLITE_ROW) {
numeroqdonnee = sqlite3_column_int(sql1Statement, 0);
NSLog(#"numeroqdonnee = %d", numeroqdonnee);
}
if (returnCode != SQLITE_DONE)
NSLog(#"Problem with step statement: %s", sqlite3_errmsg(database1));
sqlite3_finalize(sql1Statement);
}
Note, I'd suggest you log the sqlite3_errmsg if you have any problems (otherwise you're just flying blind). I've also added the sqlite3_finalize (which your original code sample may have had, but I just wanted to make sure).
Related
I have the following code and it is implemented based on a tutorial on youtube,, but I changed some of it to meet my purpose which is inserting items hard coded .. the database is created and also the table but I found that addItems is not adding.
I don't think that there is wrong with addItemToTable method,
I think it is a logical error in the first part listed here, but can not find it .Any help will be great
thanks in advance
these are the items:
table_ok = YES;
if (table_ok) {
if (!db_open_status) {
[self openDBWithSQLName:dataBaseName];
NSLog(#"DB opened");
}
NSMutableDictionary *objectColsVals = [[NSMutableDictionary alloc]init];
NSString *this_id = #"12";
NSString *this_name = #"and";
NSString *this_email = #"123#hotmail.com";
NSString *this_password = #"aa11111";
NSString *this_role = #"Marketing";
[objectColsVals setValue:this_id forKey:[my_columns_names objectAtIndex:0]];
[objectColsVals setValue:this_name forKey:[my_columns_names objectAtIndex:1]];
[objectColsVals setValue:this_email forKey:[my_columns_names objectAtIndex:2]];
[objectColsVals setValue:this_password forKey:[my_columns_names objectAtIndex:3]];
[objectColsVals setValue:this_role forKey:[my_columns_names objectAtIndex:4]];
if ([[objectColsVals allKeys] count] > 0) {
if ([self addItemToTable:tableName WithColumnValues:objectColsVals]) {
NSLog(#"inserted");
[self closeDB];
}
}
This the method:
-(BOOL)addItemToTable:(NSString *)usetable WithColumnValues:(NSDictionary *)valueObject{
BOOL has_beenAdded = NO;
NSString *mycolumns = #"";
NSString *myvalues = #"";
//loop through all the value keys
for (int r=0; r<[[valueObject allKeys] count]; r++) {
NSString *this_keyname = [[valueObject allKeys]objectAtIndex:r];
mycolumns = [mycolumns stringByAppendingString:this_keyname];
NSString *thisval = [NSString stringWithFormat:#"'%#'",[valueObject objectForKey:this_keyname]];
myvalues = [myvalues stringByAppendingString:thisval];
//add commas to seperate the col and val lists before last item
if (r<(([[valueObject allKeys] count])-1)) {
mycolumns = [mycolumns stringByAppendingString:#","];
myvalues = [myvalues stringByAppendingString:#","];
}
}
NSString *myinsert = [NSString stringWithFormat:#"INSERT INTO %# (%#) VALUES(%#)",usetable,mycolumns,myvalues];
char *err;
if (sqlite3_exec(estate_db, [myinsert UTF8String], NULL, NULL, &err) != SQLITE_OK) {
sqlite3_close(estate_db);
}else{
has_beenAdded = YES;
}
return has_beenAdded;
}
In terms of what you've got here, it's hard to say where the problem is. Nothing seems obviously wrong. At a very minimum, one should:
examine what the resulting INSERT statement that you built programmatically, to make sure there isn't some subtle issue that eludes a cursory examination of the code;
if any sqlite3_xxx() calls fail (notably if the sqlite3_exec returns anything besides SQLITE_OK), then log the error message (either the err variable, or by calling sqlite3_errmsg()); if you don't look at these error messages you're just flying blind; and
run the app on the simulator and then open the simulator's copy of the database on your Mac (in the ~/Library/Application Support/iPhone Simulator directory; if the ~/Library folder is hidden, unhide it by running the with chflags -nohidden ~/Library command in the Terminal command line tool) and examine the contents of the database directly. Verify column names, table names, etc.
Again, it's unclear where the problem is, but it likely rests in something simple like some confusion when opening the database or creating the table in question. Until we confirm error messages and the actual SQL, it's hard to say. It could be anything from an attempt to open the readonly copy of the database in the bundle to erroneously calling sqlite3_open and unwittingly creating a new blank database. You really should update the question and share the code that creates the database (or copies it from the bundle), as well as doing some of the diagnostic steps outlined above.
Having said this, I really discourage you from adding values into your SQL with stringWithFormat. The dynamic building of the SQL is fine, but you really should not the use of stringWithFormat to insert the values into the SQL, itself. Given that you're quoting the text values with single quotes, what if the person's last name was O'Brian? Or if you changed your routine to use double quotes, what if the person's name was Dwayne "The Rock" Johnson? The current code may fail if the string delimiter occurs in the data value. Worse, you technically expose yourself to SQL injection attacks.
What you should generally do is to use ? placeholders. For example, consider a dictionary defined as follows:
NSDictionary *dataToInsert = #{#"name" : #"Jack",
#"id" : #37,
#"password" : #"feefifofum",
#"role" : [NSNull null],
#"email" : #"jack#magicbeans.fairyland.com",
#"hourly_wage" : #12.85};
What you want to do is to build a SQL statement that looks like the following:
INSERT INTO test (name,id,password,role,email,hourly_wage) VALUES (?,?,?,?,?,?)
You then want to bind the values to those ? placeholders using the sqlite3_bind_xxx() functions.
So, you can create and prepare that SQL statement (building an array of values and an array of placeholders) like so:
NSArray *keys = [dataToInsert allKeys];
NSMutableArray *values = [NSMutableArray arrayWithCapacity:[keys count]];
NSMutableArray *placeholders = [NSMutableArray arrayWithCapacity:[keys count]];
// build array of values and array of question mark placeholders
for (NSString *key in keys) {
[values addObject:[dataToInsert objectForKey:key]];
[placeholders addObject:#"?"];
}
// use the `keys` and `placeholders` arrays to build the SQL
NSString *insertSql = [NSString stringWithFormat:#"INSERT INTO %# (%#) VALUES (%#)",
tableName,
[keys componentsJoinedByString:#","],
[placeholders componentsJoinedByString:#","]];
if (sqlite3_prepare_v2(db, [insertSql UTF8String], -1, &statement, NULL) != SQLITE_OK) {
NSLog(#"prepare failed: %s", sqlite3_errmsg(db));
sqlite3_close(db);
return;
}
// statement is prepared, but we still have to bind the values...
You can then bind the values with something like the following. This is doing dynamic checking of the class of the objects in the values array (and if it is a NSNumber, look at the objCType to determine the type of number):
// now use the `values` array to bind values to the ? placeholders
[values enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
int rc = SQLITE_ERROR;
if ([obj isKindOfClass:[NSString class]])
rc = sqlite3_bind_text(statement, idx + 1, [obj UTF8String], -1, SQLITE_TRANSIENT);
else if ([obj isKindOfClass:[NSNull class]])
rc = sqlite3_bind_null(statement, idx + 1);
else if ([obj isKindOfClass:[NSNumber class]]) {
const char *objCType = [obj objCType];
if (strcmp(objCType, #encode(int)) == 0 || strcmp(objCType, #encode(unsigned int)) == 0 || strcmp(objCType, #encode(short)) == 0 || strcmp(objCType, #encode(unsigned short)) == 0 || strcmp(objCType, #encode(char)) == 0 || strcmp(objCType, #encode(unsigned char)) == 0)
rc = sqlite3_bind_int(statement, idx + 1, [obj integerValue]);
else if (strcmp(objCType, #encode(long)) == 0 || strcmp(objCType, #encode(unsigned long)) == 0 || strcmp(objCType, #encode(long long)) == 0 || strcmp(objCType, #encode(unsigned long long)) == 0)
rc = sqlite3_bind_int64(statement, idx + 1, [obj longLongValue]);
else if (strcmp(objCType, #encode(float)) == 0 || strcmp(objCType, #encode(double)) == 0)
rc = sqlite3_bind_double(statement, idx + 1, [obj doubleValue]);
else {
NSLog(#"column %d is %# but has unknown numeric type %s; will use `description`", idx + 1, obj, objCType);
rc = sqlite3_bind_text(statement, idx + 1, [[obj description] UTF8String], -1, SQLITE_TRANSIENT);
}
}
else
rc = sqlite3_bind_text(statement, idx + 1, [[obj description] UTF8String], -1, SQLITE_TRANSIENT);
if (rc != SQLITE_OK)
{
NSLog(#"bind %d failed: %s", idx + 1, sqlite3_errmsg(db));
sqlite3_finalize(statement);
sqlite3_close(db);
return;
}
}];
if (sqlite3_step(statement) != SQLITE_DONE) {
NSLog(#"step failed: %s", sqlite3_errmsg(db));
sqlite3_close(db);
return;
}
sqlite3_finalize(statement);
How can I write this query without using stringWithFormat. How can I pass parameters to the SQLite Query. Now my code is this:
NSString *querySQL = [NSString stringWithFormat:#"SELECT name, char_code, sound, status From Tmy_table Where ID=\"%d\"", i];
Thanks in advance
You should use sqlite3 host parameters and sqlite3_bind() to bind variables to them. This would like something like this in your example.
NSString* query = #"SELECT name, char_code, sound, status From Tmy_table Where ID=?";
sqlite3_stmt* myStatement = NULL;
sqlite3_prepare_v2(myDBConnection, [query UTF8String], -1, &myStatement, NULL);
sqlite3_bind_int(myStatement, 1, i);
Points to note:
The two sqlite3 functions return error codes that you must check. I've left that out for clarity.
The second parameter in sqlite3_bind_int() tells you which question mar to replace with the third parameter. The index starts at 1, not 0.
See also docs about binding.
NSString sql = #"SELECT name, char_code, sound, status From Tmy_table Where ID=?";
sqlite3_stmt *stmt = NULL;
if (sqlite3_prepare_v2(database, [sql UTF8String], -1, &stmt, SQLITE_STATIC) == SQLITE_OK)
{
// If 'i' was text:
// if (sqlite3_bind_text(stmt, 1, i, -1, SQLITE_STATIC) == SQLITE_OK)
if (sqlite3_bind_int(stmt, 1, i) == SQLITE_OK) // Note: 1-based column when binding!!!!
{
while (sqlite3_step(stmt) == SQLITE_ROW)
{
const char *name = sqlite3_column_text(stmt, 0); // Note: 0-based column when fetching!!!
const char *sound = sqlite3_column_text(stmt, 1);
const char *status = sqlite3_column_text(stmt, 2);
// ... print the values or whatever
}
}
else
{
NSLog(#"Failed to bind int: %s", sqlite3_errmsg(database));
}
sqlite3_finalize(stmt);
}
else
{
NSLog(#"Failed to prepare statement '%#': %s", sql, sqlite3_errmsg(database));
}
EDIT Changed the bind to sqlite3_bind_text() as i appears to be text...
Very confused by this... my "DiscoveredCars" table begins with two records in it. The first time I call this method with it inserts properly and the last line displays "3" for the number of records.
Yet the second time I call it, the SQL looks perfect yet for some reason it still is showing "3" for the size and does not seem to be inserting properly? Any ideas?
- (void)writeToDiscoveredCars: (Car *)car {
NSString *tempSQL = [NSString stringWithFormat:#"INSERT INTO DiscoveredCars (car_ID) VALUES (%i)", car.ID];
NSLog(#"size of discoveredis %i", [self numberRecordsForTable:#"DiscoveredRecipes"]);
NSLog(#"SQL insert is %#", tempSQL);
const char *sql = [tempSQL UTF8String];
sqlite3_stmt *statement;
int sqlResult = sqlite3_prepare_v2(myDatabase, sql, -1, &statement, NULL);
if (sqlResult == SQLITE_OK) {
sqlResult = sqlite3_step(statement);
if(sqlResult == SQLITE_DONE)
{
}
sqlite3_finalize(statement);
}
else
{
NSLog(#"1. problem with database");
NSLog(#"%s", sqlite3_errmsg(myDatabase));
}
NSLog(#"size of discoveredis %i", [self numberRecordsForTable:#"DiscoveredCars"]);
}
You never execute your query by calling sqlite3_step.
int sqlResult = sqlite3_prepare_v2(myDatabase, sql, -1, &statement, NULL);
if (sqlResult == SQLITE_OK) {
sqlResult = sqlite3_step(statement); //Execute!
//check the result for completion
if(sqlResult == SQLITE_DONE)
{
}
sqlite3_finalize(statement);
}
I'm developing an iPhone app. I've got a function that reads data from a sqlite database and puts the results into an array. Everything works fine. Here is part of the function that fills the array:
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSString *aVar1 = [NSString stringWithUTF8String(char*)sqlite3_column_text(compiledStatement, 0)];
NSString *aVar2 = [NSString stringWithUTF8String(char*)sqlite3_column_text(compiledStatement, 1)];
NSArray *anArray = [NSArray arrayWithObjects:aVar1,aVar2,nil];
[returnArray addObject:anArray]
[anArray release];
}
//return the array
I want to make this function more generic so that it takes a sql statement string as a parameter, and returns a mutablearray of arrays, no matter how many columns are in the result set.
Is there a way to do this? The solution doesn't have to include arrays -- could be any collection object. I'm just looking for a way to make the function re-usable for other queries to the same database.
Couldn't you just do something like:
int numCols = sqlite3_column_count(compiledStatement);
NSMutableArray *result = [NSMutableArray array];
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
NSMutableArray *array = [NSMutableArray array];
for (int i = 0; i < numCols; i++) {
[array addObject:
[NSString stringWithUTF8String:
(char *)sqlite3_column_text(compiledStatement, i)]];
}
[result addObject:array];
}
+(NSArray *)executeQueryAndReturnArray:(NSString *)query {
sqlite3_stmt *statement = nil;
const char *sql = [query UTF8String];
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) != SQLITE_OK) {
NSLog(#"[SQLITE] Error when preparing query!");
} else {
NSMutableArray *result = [NSMutableArray array];
while (sqlite3_step(statement) == SQLITE_ROW) {
NSMutableArray *row = [NSMutableArray array];
for (int i = 0; i < sqlite3_column_count(statement); i++) {
int colType = sqlite3_column_type(statement, i);
id value;
if (colType == SQLITE_TEXT) {`enter code here`
const unsigned char *col = sqlite3_column_text(statement, i);
value = [NSString stringWithFormat:#"%s", col];
} else if (colType == SQLITE_INTEGER) {
int col = sqlite3_column_int(statement, i);
value = [NSNumber numberWithInt:col];
} else if (colType == SQLITE_FLOAT) {
double col = sqlite3_column_double(statement, i);
value = [NSNumber numberWithDouble:col];
} else if (colType == SQLITE_NULL) {
value = [NSNull null];
} else {
NSLog(#"[SQLITE] UNKNOWN DATATYPE");
}
[row addObject:value];
}
[result addObject:row];
}
return result;
}
return nil;
}
I'm trying the following code to count the number of rows in my SQLite database table, but it throws an exception. Is these a simpler way to do this?
- (void) countRecords {
int rows = 0;
#try {
NSString *dbPath = [self getDBPath];
if (sqlite3_open([dbPath UTF8String], &database) == SQLITE_OK) {
NSString *strSQL;
strSQL = #"SELECT COUNT(*) FROM MYTABLE";
const char *sql = (const char *) [strSQL UTF8String];
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(database, sql, -1, &stmt, NULL) == SQLITE_OK) {
// THIS IS WHERE IT FAILS:
if (SQLITE_DONE!=sqlite3_step(stmt) ) {
NSAssert1(0,#"Error when counting rows %s",sqlite3_errmsg(database));
} else {
rows = sqlite3_column_int(stmt, 0);
NSLog(#"SQLite Rows: %i", rows);
}
sqlite3_finalize(stmt);
}
sqlite3_close(database);
}
}
#catch (NSException * e) {
NSLog(#"Error Counting");
}
}
I came across a solution, using my code above, just replacing the step statement with the code below:
if (sqlite3_step(stmt) == SQLITE_ERROR) {
NSAssert1(0,#"Error when counting rows %s",sqlite3_errmsg(database));
} else {
rows = sqlite3_column_int(stmt, 0);
NSLog(#"SQLite Rows: %i", rows);
}
This usually works for me
- (NSInteger )numberRecordsForTable:(NSString *)table {
NSInteger numTableRecords = -1;
if (sqlite3_open([self.dbPath UTF8String], &database) == SQLITE_OK) {
NSString *sqlStatement = [NSString stringWithFormat: #"select count(*) from %#", table];
const char *sql = [sqlStatement cStringUsingEncoding:NSUTF8StringEncoding];
if(sqlite3_prepare_v2(database, sql, -1, &sqlClause, NULL) == SQLITE_OK) {
while(sqlite3_step(sqlClause) == SQLITE_ROW) {
numTableRecords = sqlite3_column_int(sqlClause, 0);
}
}
else {
printf("could not prepare statement: %s\n", sqlite3_errmsg(database));
}
}
else {
NSLog(#"Error in Opening Database File");
}
sqlite3_close(database);
return numTableRecords;
}
HTH
There is no SQL expression to count rows in a database: you can count rows in a every table and then add them up.
I thought I'd trow in my two cents here as there is an expression to count rows in a database, I use it when dealing with MySQL databases using php scripts all the time. and I tested it in an ios app it's available in there too behold:
sqlite3 *database;
if(sqlite3_open([dbpath UTF8String], &database) == SQLITE_OK)
{
NSString *sql = #"select count(*) from today";
sqlite3_stmt *selectStatement;
int returnValue = sqlite3_prepare_v2(database, [sql UTF8String], -1, &selectStatement, NULL);
if (returnValue == SQLITE_OK)
{
if(sqlite3_step(selectStatement) == SQLITE_ROW)
{
numrows= sqlite3_column_int(selectStatement, 0);
}
}
sqlite3_finalize(selectStatement);
sqlite3_close(database);
}
no need for a fancy loop counter thing. btw if your using an auto increment int for the primary key. it works just slightly different then an array's key. where as in an array that is n items long the valid array elements are from 0 to n-1 in a database the key field is from 1 to n simple enough to work around if you just keep that in mind.
-(void)databaseRecordCount{
int rows = 0;
#try {
sqlite3 *database;
NSString *filePath = [self databaseDocumentsFilePath];
if(sqlite3_open([filePath UTF8String], &database) == SQLITE_OK) {
NSString *query = #"SELECT * FROM MYTABLE";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &compiledStatement, NULL) != SQLITE_OK)
NSLog(#"Error while creating detail view statement. '%s'", sqlite3_errmsg(database));
if(sqlite3_prepare_v2(database, [query UTF8String], -1, &compiledStatement, nil) == SQLITE_OK) {
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
rows++;
}
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
}
#catch (NSException * e) {
NSLog(#"Error Counting");
}
NSLog(#"SQLite Rows: %i", rows);
NSUserDefaults *userDefaults;
userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setInteger:rows forKey:#"databaseRecordCount"];
[userDefaults synchronize];
}
You'll have to count of each table individually. Some pseudo code:
sql = "SELECT name FROM sqlite_master" WHERE type = 'table'
tables() = GetRows(sql)
Dim total As Integer
For Each t As String in tables
sql = "SELECT COUNT(*) FROM " + t
total = total + GetValue(sql)
Next
Show(total)