I am trying to insert multiple row data same time to the sqlite database.But for example I have a value.I am getting that from my api.And in this value I have 2000 data.I am just adding 1 row and in that row I Can see 2000 data.
for (NSDictionary *customerDictionary in customerArray) {
Kart *kart = [Kart customerWithName:[customerDictionary valueForKey:#"adi"]];
[_kartList addObject:kart];
}
And I am using FMDB for the sqlite.
EDIT
I can add the one data to the sqlite db with that.But when I try to add another object to the database its adding all datas to the my database in one row.
Its just adding one row and correct data
[database executeUpdate:#"INSERT OR REPLACE INTO KartDB (adi) VALUES (?)" withArgumentsInArray:kart.adi];
Its adding all data from kart.adi and kart.adi2 in one row.
[database executeUpdate:#"INSERT OR REPLACE INTO KartDB (adi,adi2) VALUES (?,?)" withArgumentsInArray:[NSArray arrayWithObjects:kart.adi,kart.adi2, nil]];
Can you give me a suggestion ?
What's your PRIMARY KEY for the table you're trying to add the data into? Perhaps you're simply overriding inserts because the PRIMARY KEY isn't unique.
Would look something like this.
[database executeUpdate:#"INSERT OR REPLACE INTO KartDB (primaryKey, adi,adi2) VALUES (?,?,?)" withArgumentsInArray:[NSArray arrayWithObjects:primaryKey, kart.adi,kart.adi2, nil]];
Also, if you're doing 2000 inserts at once, look into FMDB's inTransaction feature. Will make your updates much faster.
You could then call something like:
[dbManager.databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
for (NSDictionary *customerDictionary in customerArray) {
Kart *kart = [Kart customerWithName:[customerDictionary valueForKey:#"adi"]];
[database executeUpdate:#"INSERT OR REPLACE INTO KartDB (primaryKey, adi) VALUES (?,?)", primaryKey, kart.adi];
}
}];
[dbManager.databaseQueue close];
Related
I am using Realm database for iOS application where i have a use case in which i want to filter result set by distinct values for a particular field. This field is not a primary key of the realm table.
I was not able to construct query for that.
Sample query :
RLMResults *allFiles = [FileRLMObject objectsInRealm:realmObject where:#"colA == %#", #"test1"];
FileRLMObject is a subclass of RLMObject from realm library
here table contains one column with name colB. While getting allFiles results, i want to get rows which are having distinct colB values.
Any suggestions how i can achieve this?
Realm doesn't support distinct queries yet. You can subscribe issue #1103 to track progress on that.
As a workaround, you could query for all values for colB first and then select objects for each value of it, as seen below:
NSArray *values = [FileRLMObject.allObjects valueForKey:"type"];
NSSet *distinctValues = [NSSet setWithArray:values];
NSMutableArray *allFiles = [NSMutableArray new];
for (NSString *colB in distinctValues) {
// This takes the firstObject.
// You might want to modify the sort order to make sure
// you get a certain object in case that there may exist
// multiple objects per distinct value.
FileRLMObject *object = [FileRLMObject objectsWhere:#"colB == ?", colB].firstObject;
[allFiles appendObject:object];
}
I am trying to order UITableView rows. I already implemented necessary methods and it works on my mutable array. However I don't know how can I apply this changes to my database.
My database has favs column
CREATE TABLE favs (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
w_name TEXT
);
I was using "insert or replace into favs (w_name) values ('%#%');" statement to insert new word to favs database. How can I change the id of two records so that I can list the favs values by order of id so that they will be sorted. For example I want to change my table from this
id|w_name
1|apple
2|banana
3|orange
to
id|w_name
1|apple
3|banana
2|orange
I can change the id if the key is not primary by using
UPDATE favs SET id = (CASE id WHEN 2 THEN 3 ELSE 2 END)
WHERE id IN (2, 3);
If I do like this I need to calculate id by myself each time I insert the record. So, how can re-order ids so that my changes in UITableView is reflected into database.
In all case, if you want to reorder you TableView, you've to reorder the array that feed the TableView.
You can execute the query again.
Or use that
yourArray = [yourArray sortedArrayUsingComparator:^NSComparisonResult(TYPE *p1, TYPE *p2){
return [p1 compare:p2];
}];
and finish with [tableView reloadData];
Add another column to your table and call it sort. There you will put the index of each record in your array [array indexOfObject:object]. Every time you want to save the order of your array you can update the sort column of each record e.g.
const char *sql = "UPDATE favs SET sort = ? WHERE id = ?";
if(sqlite3_prepare_v2(database, sql , -1, &updateStatment, NULL) != SQLITE_OK)
NSAssert1(0, #"Error while creating update statement. '%s'", sqlite3_errmsg(database));
sqlite3_bind_int(updateStatment, 1, [array indexOfObject:object]); sqlite3_bind_int(updateStatment, 2, [object id]);
I'm hoping someone here might be able to help me out.
I need to insert a NSMutableDictionary into one cell of a SQLite database. I am able to insert strings etc into the database, but when I try and insert a Dictionary I get a syntax error:
**Can't run query 'BEGIN TRANSACTION; UPDATE Database SET Column1 = {
Bad = "";
"End_Time" = 4;
Good = "";
Moderate = "";
Note = "";
} WHERE Title = Name; COMMIT TRANSACTION;' error message: unrecognized token: "{"**
To do this I am using the following code:
NSString *sql2 = [NSString stringWithFormat:#"UPDATE Database SET Column%# = %# WHERE Title = %#",previousQuestion,adictionary,Name];
Can anyone help or suggest a different approach,? I need to be able to store the key/values in 1 cell, as there will be 79 more cells with similar data and I need to reference each specific key from a specific column of the database.
I've tried turning the Dictionary into a string (and then I'd turn the string back to a dictionary on retrieval) But this causes the same issue.
Any suggestions?
Many thanks,
Andrew
Their is two way you can do this.
1st way :-
Archive your NSMutableDictionary convert it into NSData and store it in your Sqlite column which datatype should have be blob type. Archiving something like this,
NSData *theDictionaryData = [NSKeyedArchiver archivedDataWithRootObject:yourDictionary];
Bind this data in sqlite,
sqlite3_bind_blob(addStmt, 5, theDictionaryData, -1, SQLITE_TRANSIENT);
Now retrieving time from sqlite,
NSData *retrieveData = [[NSData alloc] initWithBytes:sqlite3_column_blob(selectstmt, 4) length:sqlite3_column_bytes(selectstmt, 4)];
and finally convert it into NSDictionary,
NSDictionary *dictionary = [NSKeyedUnarchiver unarchiveObjectWithData:retrieveData];
2nd way :-
Create numbers of column those are equal to numbers of keys of NSDictionary. Bring data from every key and save it respective table column.
You can't just insert an Objective-C object into the database. It must be a string, binary data, or a number. In converting it to a string, you also need to sanitize the input (so that it doesn't contain any invalid characters that SQL will interpret in another way). For example, if you tried to insert a string that was some SQL code, you wouldn't want it to try to execute that code. It looks like here it's getting hung up on a { in the string. You could instead convert the dictionary or the string to NSData and insert it into the database as binary data.
I'm using FMDB to create a SQLite database on iPhone. I have a initial.sql that is of the form
CREATE TABLE Abc ... ;
CREATE TABLE Def ... ;
I load this by loading the file into an NSString and running it
NSString * str = // string from file initial.sql
[db executeUpdate: str];
This succeeds but later on I get a failure:
no such table: Def
It's clear that the second statement is not being called. How can I do this so that all of the queries will be called?
According to the SQLite documentation:
"The routines sqlite3_prepare_v2(), sqlite3_prepare(), sqlite3_prepare16(), sqlite3_prepare16_v2(), sqlite3_exec(), and sqlite3_get_table() accept an SQL statement list (sql-stmt-list) which is a semicolon-separated list of statements."
So, this should all work.
I got bitten by this one too; it took me an entire morning of stepping through FMDatabase and reading the sqlite3 API documentation to find it. I am still not entirely sure about the root cause of the issue, but according to this bug in PHP, it is necessary to call sqlite3_exec instead of preparing the statement with sqlite3_prepare_v2 and then calling sqlite3_step.
The documentation does not seem to suggest that this behaviour would happen, hence our confusion, and I would love for someone with more experience with sqlite to come forward with some hypotheses.
I solved this by developing a method to execute a batch of queries. Please find the code below. If you prefer, you could rewrite this into a category instead of just adding it to FMDatabase.h, your call.
Add this to the FMDatabase interface in FMDatabase.h:
- (BOOL)executeBatch:(NSString*)sql error:(NSError**)error;
Add this to the FMDatabase implementation in FMDatabase.m:
- (BOOL)executeBatch:(NSString *)sql error:(NSError**)error
{
char* errorOutput;
int responseCode = sqlite3_exec(db, [sql UTF8String], NULL, NULL, &errorOutput);
if (errorOutput != nil)
{
*error = [NSError errorWithDomain:[NSString stringWithUTF8String:errorOutput]
code:responseCode
userInfo:nil];
return false;
}
return true;
}
Please note that there are many features missing from executeBatch which make it unsuitable for a lot of purposes. Specifically, it doesn't check to see if the database is locked, it doesn't make sure FMDatabase itself isn't locked, it doesn't support statement caching.
If you need that, the above is a good starting point to code it yourself. Happy hacking!
FMDB v2.3 now has a native wrapper for sqlite3_exec called executeStatements:
BOOL success;
NSString *sql = #"create table bulktest1 (id integer primary key autoincrement, x text);"
"create table bulktest2 (id integer primary key autoincrement, y text);"
"create table bulktest3 (id integer primary key autoincrement, z text);"
"insert into bulktest1 (x) values ('XXX');"
"insert into bulktest2 (y) values ('YYY');"
"insert into bulktest3 (z) values ('ZZZ');";
success = [db executeStatements:sql];
It also has a variant that employs the sqlite3_exec callback, implemented as a block:
sql = #"select count(*) as count from bulktest1;"
"select count(*) as count from bulktest2;"
"select count(*) as count from bulktest3;";
success = [db executeStatements:sql withResultBlock:^int(NSDictionary *dictionary) {
NSInteger count = [dictionary[#"count"] integerValue];
NSLog(#"Count = %d", count);
return 0; // if you return 0, it continues execution; return non-zero, it stops execution
}];
Split Batch Statement
Add in .h file:
#import "FMSQLStatementSplitter.h"
#import "FMDatabaseQueue.h"
FMSQLStatementSplitter can split batch sql statement into several separated statements, then [FMDatabase executeUpdate:] or other methods can be used to execute each separated statement:
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:databasePath];
NSString *batchStatement = #"insert into ftest values ('hello;');"
#"insert into ftest values ('hi;');"
#"insert into ftest values ('not h!\\\\');"
#"insert into ftest values ('definitely not h!')";
NSArray *statements = [[FMSQLStatementSplitter sharedInstance] statementsFromBatchSqlStatement:batchStatement];
[queue inDatabase:^(FMDatabase *adb) {
for (FMSplittedStatement *sqlittedStatement in statements)
{
[adb executeUpdate:sqlittedStatement.statementString];
}
}];
While building a Search for my app i ran into a problem whilst using the FMDB SQLite Wrapper (https://github.com/ccgus/fmdb).
When I search my database with this SQL Command, everything is fine. 13 objects are returned and I can use them.
FMResultSet *rs = [db executeQuery:#"SELECT * FROM ZARTICLE WHERE ZTITLEDE LIKE '%Daimler%'"];
But when i try to insert the searchQuery from the User Input like this:
FMResultSet *rs = [db executeQuery:#"SELECT * FROM ZARTICLE WHERE ZTITLEDE LIKE (?)", theSearchQuery];
... the value is dont be inserted into SQL Command. And I dont get any returned objects from the DB. even if the String (theSearchQuery) is the same written in the first example.
Additionaly I post a part from the documentation of FMDB for your convinience. :)
Data Sanitization
When providing a SQL statement to FMDB, you should not attempt to "sanitize" any values before insertion. Instead, you should use the standard SQLite binding syntax:
INSERT INTO myTable VALUES (?, ?, ?)
The ? character is recognized by SQLite as a placeholder for a value to be inserted. The execution methods all accept a variable number of arguments (or a representation of those arguments, such as an NSArray or a va_list), which are properly escaped for you.
Thus, you SHOULD NOT do this (or anything like this):
[db executeUpdate:[NSString stringWithFormat:#"INSERT INTO myTable VALUES (%#)", #"this has \" lots of ' bizarre \" quotes '"]];
Instead, you SHOULD do:
[db executeUpdate:#"INSERT INTO myTable VALUES (?)", #"this has \" lots of ' bizarre \" quotes '"];
All arguments provided to the -executeUpdate: method (or any of the variants that accept a va_list as a parameter) must be objects. The following will not work (and will result in a crash):
[db executeUpdate:#"INSERT INTO myTable VALUES (?)", 42];
The proper way to insert a number is to box it in an NSNumber object:
[db executeUpdate:#"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:42]];
Alternatively, you can use the -execute*WithFormat: variant to use NSString-style substitution:
[db executeUpdateWithFormat:#"INSERT INTO myTable VALUES (%d)", 42];
Internally, the -execute*WithFormat: methods are properly boxing things for you. The following percent modifiers are recognized: %#, %c, %s, %d, %D, %i, %u, %U, %hi, %hu, %qi, %qu, %f, %g, %ld, %lu, %lld, and %llu. Using a modifier other than those will have unpredictable results. If, for some reason, you need the % character to appear in your SQL statement, you should use %%.
NSString *search_text = [NSString stringWithFormat:#"%%%#%%", theSearchQuery];
FMResultSet *rs = [db executeQuery:#"SELECT * FROM ZARTICLE WHERE ZTITLEDE LIKE ?", search_text];
I would highly recommend to avoid creating queries with stringWithFormat:! There is a good reason why FMDB tries to force you to use their data sanitization. However, since FMDB is boxing your input, surrounding parenthesis in the following code are not needed and may cause your problem.
[db executeQuery:#"SELECT * FROM ZARTICLE WHERE ZTITLEDE LIKE (?)", theSearchQuery];
Simple add arguments without any parenthisis because you never know how FMDB boxes your argument internally.
[db executeQuery:#"SELECT * FROM ZARTICLE WHERE ZTITLEDE LIKE ?", theSearchQuery];
If this still doesn't work try to use the suggested executeQueryWithFormat: method of FMDB:
[db executeQueryWithFormat:#"SELECT * FROM ZARTICLE WHERE ZTITLEDE LIKE %#", theSearchQuery];