I have the follow SQL statement:
const char* sqlStatement = "SELECT ((? - deal.latitude) * (? - deal.latitude) + (? - deal.longitude) * (? - deal.longitude)) AS distance, id, title, shop, latitude, longitude FROM deal WHERE (type = ?) AND category IN (?) AND tribe IN (?) ORDER BY distance LIMIT 20;";
// ...
sqlite3_bind_double(preparedStatement, 1, location.latitude);
sqlite3_bind_double(preparedStatement, 2, location.latitude);
sqlite3_bind_double(preparedStatement, 3, location.longitude);
sqlite3_bind_double(preparedStatement, 4, location.longitude);
sqlite3_bind_int(preparedStatement, 5, type);
sqlite3_bind_text(preparedStatement, 6, [categories UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(preparedStatement, 7, [tribes UTF8String], -1, SQLITE_TRANSIENT);
Here, sixth and seventh arguments cause the query to fail, I mean the block inside my
while (sqlite3_step(preparedStatement) == SQLITE_ROW) {
isn’t executed. categories and tribes are build as follow:
NSArray* userCategories = [CategoryDataController getUserCategories];
NSMutableString* categories = [[NSMutableString alloc] init];
for (NSNumber *category in userCategories) {
[categories appendString:[[NSString alloc] initWithFormat:@"%@, ", category]];
}
if ([categories length] > 0) {
categories = (NSMutableString *)[categories substringToIndex:[categories length] - 2];
}
NSArray* userTribes = [TribeDataController getUserTribes];
NSMutableString* tribes = [[NSMutableString alloc] init];
for (NSNumber* tribe in userTribes) {
[tribes appendString:[[NSString alloc] initWithFormat:@"%@, ", tribe]];
}
if ([tribes length] > 0)
tribes = (NSMutableString *)[tribes substringToIndex:[tribes length] - 2];
userCategories and userTribes are arrays of NSNumber, and if I log tribes and categories I get well formed strings, that is something like:
1, 2, 3, 4, 5
The strange thing is that instead use the sqlite3_bind_ functions I build the query as follow:
NSString *sqlStatementNSString = [[NSString alloc] initWithFormat:@"SELECT ((%f - deal.latitude) * (%f - deal.latitude) + (%f - deal.longitude) * (%f - deal.longitude)) AS distance, id, title, shop, latitude, longitude FROM deal WHERE type = %d AND category IN (%@) AND tribe IN (%@) ORDER BY distance LIMIT 20;", location.latitude, location.latitude, location.longitude, location.longitude, type, categories, tribes];
const char *sqlStatement = [sqlStatementNSString UTF8String];
it works! What I’m doing wrong? Thanks beforehand (and please, sorry for my English).
I’ve searched for an answer to this, as well, and it seems that, in general, prepared statements don’t support the IN operator. There are all sorts of solutions which you can find on this site. My preferred solution (which was obtained from one of the answers) is to prepare a statement with a fixed number of slots in the IN operator list:
Then you perform the query ((n + 5) / 6) times (in this example) and merge all the answers. If you have less than the number of slots, fill the rest with NULL, or duplicate the last entry. The optimizer should, hopefully, avoid the duplicate comparisons.
Reusing a prepared statement is probably faster than building and running a custom query string (but profile to be sure!)