I'm having some difficulty getting my database updated. Basically the user will input data into two separate places, so we get
Name | Letter | Marks
----------------------------
Dave | Null | 90
Dave | A | Null
which should become
Dave | A | 90
However, nothing is updating. The query works perfectly when I try it in SQLite Manager, so I must be implementing cursor wrong.
public void insertData(String name, int mark_column, String marks) {
String [] columns = new String[] {COL_3, COL_4, COL_5};
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(COL_2, name);
contentValues.put(columns[mark_column], marks);
db.replace(TABLE_NAME, null, contentValues);
//The code above works as desired
String sql = "SELECT NAME, GROUP_CONCAT(LETTER, ', ') AS LETTER," +
"GROUP_CONCAT(MARKS, ', ') AS MARKS FROM " + TABLE_NAME + " GROUP BY NAME";
//This query works in SQLite Manager
Cursor c = db.rawQuery(sql, null);
c.moveToFirst();
while(c.moveToNext());
c.close();
}
I have tried various combinations of c.moveToLast, not having c.moveToNext, etc. This method is called in onClick of an Alert Dialog.
Any help is greatly appreciated
Regarding the cursor:
I don't see anything wrong with your query. If you aren't "seeing" any results in your app, it's likely because you aren't actually doing anything with the results. They exist in memory in a Cursor object, but that's all; if you want to see anything you have to bind that data to some UI components, or dump it to logcat, or something.
Note that if you were to add code inside of your while loop, you would skip the first row of the cursor because you would have a moveToFirst() call followed immediately by a moveToNext() call. This is how I iterate over a Cursor, and it always works:
if (cusor != null) {
try {
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
// do something with data in current row
}
} finally {
cursor.close();
}
}
Regarding the update:
You actually aren't doing an update per se, you are doing an insert. SQLiteDatabase.replace() executes this command:
INSERT OR REPLACE INTO tableName(columns) VALUES (values);
This can work as an update only if you have a constraint on the table and the insertion of a new row with these values would violate that constraint (the exact handling for different constraint violations is described here). For the constraint types that I suspect you are expecting, this operation will delete the existing row and insert a new row with these values, but it will not carry over values from the deleted row into the new one. In other words, you need all the combined values in the ContentValues if you expect a replace to occur. It's not like an UPDATE where you can set the values of just certain columns.
You should probably try to do an actual update instead. Make sure to use a proper WHERE clause so you only update rows that matter.
I may be misunderstanding your approach, but the description makes it seem like you are inserting two rows, then trying to update and/or combine them both later. This doesn't make sense to me, and I foresee bugs whereby you have leftover rows that are incomplete and need to be cleaned up. In my opinion, it's better to structure the code so there is one INSERT, and every operation thereafter is an UPDATE on the row of interest.
Related
Conclusion: Android's database APIs work but the documentation is horribly incomplete.
I have recently run into a brain wrecking situation due to the flexibility Sqlite provides by not forcing you to specify the data type when creating the table. The problem was my mindset that assumed that every data type would be a general character sequence if not specified and therefore the way to talk to database is through java.lang.String.
But you can't blame me either when you see methods like the below:
int delete (String table,
String whereClause,
String[] whereArgs)
in the SqlDatabase class from Android docs.
I have a table consisting of Phone No(that I stored as java.lang.String) and Timestamp as a long field. When I tried deleting a record using this method, it just never got deleted despite countless debugging.
I checked everything and query was alright and table is existent and all the checklist until by chance, I discovered that removing the '' around the timestamp while querying in a raw manner instead of using the above method yields a successful deletion, something like this:
DELETE FROM messages_records_table WHERE messageTimestamp = 1508494606000;
instead of the following:
DELETE FROM messages_records_table WHERE messageTimestamp = '1508494606000';
or,
DELETE FROM messages_records_table WHERE messageTimestamp = "1508494606000";
Phone No isn't a problem; it's the timestamp that was creating the problem in INSERTION/DELETION
So, I tried running a raw deletion query with quotes removed(that are required with a string/varchar type) and it yielded successful deletion. I used the following method for this:
db.execSQL(String sql, Object[] whereArgs)
The key thing to notice here is that Object[] is different from String[] when compared to delete(). I passed a Long to Object to make it work but passing a Long.toString() in delete() seems to be useless.
So my question is, Is my analysis correct and delete() API is basically useless or have I missed some bigger picture..after all, it's provided by Android team carefully?
SQLite supports multiple data types; and while column types are not strictly enforced, values might be automatically converted in some cases (this is called affinity).
When your values are stored as numbers, you should access them as numbers, not as strings.
The Android database API does not allow you to use parameter types other than strings in most functions. This is a horrible design bug.
To search for a number, either use execSQL(), which allows you to use number parameters, or convert the string value back into a number:
db.delete(..., "timestamp = CAST(? AS NUMBER)",
new String[]{ String.valueOf(ts) });
The problem was my mindset that assumed that every data type would be
a general character sequence if not specified and therefore the way to
talk to database is through java.lang.String.
I think that's the real issue.
If you specify no type e.g.
CREATE TABLE mytable (col1,col2,col3)
Then according to Determination of Column Affinity(3.1) rule 3:-
3) If the declared type for a column contains the string "BLOB" or if no
type is specified then the column has affinity BLOB.
And then according to Section 3
A column with affinity BLOB does not prefer one storage class over
another and no attempt is made to coerce data from one storage class
into another.
I've personally never had an issue with delete. However I do have a tendency to always delete according to rowid.
Here's a working example usage that shows that delete isn't useless and is deleting according to a long. However the columns are all of type INTEGER :-
int pudeletes;
int sldeletes;
int rdeletes;
int pdeletes;
if(doesProductExist(productid)) {
// if not in a transaction then begin a transaction
if(!intransaction) {
db.beginTransaction();
}
String whereargs[] = { Long.toString(productid)};
// Delete ProductUsage rows that use this product
pudeletes = db.delete(
DBProductusageTableConstants.PRODUCTUSAGE_TABLE,
DBProductusageTableConstants.PRODUCTUSAGE_PRODUCTREF_COL +
" = ?",
whereargs
);
// Delete ShopList rows that use this product
sldeletes = db.delete(
DBShopListTableConstants.SHOPLIST_TABLE,
DBShopListTableConstants.SHOPLIST_PRODUCTREF_COL +
" = ?",
whereargs
);
// Delete Rules rows that use this product
rdeletes = db.delete(
DBRulesTableConstants.RULES_TABLE,
DBRulesTableConstants.RULES_PRODUCTREF_COL +
" = ?",
whereargs
);
// Delete the Product
pdeletes = db.delete(
DBProductsTableConstants.PRODUCTS_TABLE,
DBProductsTableConstants.PRODUCTS_ID_COL +
" = ?",
whereargs
);
// if originally not in a transaction then as one was started
// complete and end the transaction
if(!intransaction) {
db.setTransactionSuccessful();
db.endTransaction();
}
}
Suppose you're trying to get values from several columns of an Android cursor (I'm using SQLite if that matters). You only care about one row, so caching the column indices doesn't make sense. You can try to get each column's value in one line like this:
String value = cursor.getString(cursor.getColumnIndex(columnName));
But how do you detect and handle a case where a column by that name doesn't exist in the cursor?
We know that if that happens, cursor.getColumnIndex(name) will return -1. But then what will cursor.getString(-1) do? Can you use try, and catch any exception thrown? It's not clear ... the documentation says,
The result and whether this method throws an exception when the column value is null or the column type is not a string type is implementation-defined.
But it doesn't say anything about exceptions or other specified behavior for a columnIndex of -1.
My testing on a few devices indicates that you get this exception:
java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow.
Make sure the Cursor is initialized correctly before accessing data from it.
Can we count on that exception being thrown?
Another way to do it is to check the index returned by getColumnIndex() before calling getString(columnIndex):
columnIndex = cursor.getColumnIndex(columnName);
if (columnIndex < 0) {
// handle absence of column
} else {
value = cursor.getString(columnIndex);
}
But using that many lines per column seems verbose, and violates the DRY principle, lending itself to careless errors in the code.
I wouldn't mind the repetition if we could extract that code into a separate method to be called for each column. But I don't see any simple way to do it, because the columns can have different types. (We could subclass CursorWrapper and implement getInt(), getString(), etc. to do the checking we want, thus confining the repetition to a specialized utility class. Is that the best we can do?)
Another approach would be using getColumnIndexOrThrow() instead of getColumnIndex(). That would allow you to skip the step of saving the value of columnIndex and testing it, because you can count on an exception being thrown if columnIndex is -1 (and catch it). But that doesn't perform very well:
If you're not sure if a column will exist or not use getColumnIndex(String) and check for -1, which is more efficient than catching the exceptions.
Any other ideas? Any thoughts on the above ideas?
Is there a concise, predictable way to get a column value from a
cursor?
I'd say yes but that would be according to good design methodology which would largely if not completely eliminate guessing/not knowing a column name. That is the column names would be known before the Cursor is created.
However, there could be situations where the column name, even if correct, could result in unanticipated results. For example where a Cursor with joined tables which had the same column name (e.g. the often used _id column) or perhaps going to the realms of insanity something like SELECT _id, _id, _id FROM cards . In such cases renaming column name via an AS clause would be the solution.
But how do you detect and handle a case where a column by that name
doesn't exist in the cursor?
Again, as above, a well designed project would likely fully negate the chance of this happening. If not, then use of the Cursor getColumnCount and getColumnName methods could be used to circumvent the -1, IllegalStateException condition e.g.
public boolean isColumnInCursor(String columName, Cursor csr) {
for (int i=0; i < csr.getColumnCount(); i++) {
if (csr.getColumnName(i).toLowerCase().equals(columName.toLowerCase())) {
csr.close();
return true;
}
}
csr.close();
return false;
}
An example usage being :-
Cursor csr = ex001db.getAisleAndShopsWithUniqueIDColumns();
String test_column = "Fred";
if (!ex001db.isColumnInCursor("Fred", csr)) {
Log.d("OUCH","Column " + test_column + " not in Cursor");
}
test_column = DBHlpr001.AISLENAMECOLUMN;
if (!ex001db.isColumnInCursor(test_column, csr)) {
Log.d("OUCH","Column " + test_column + " not in Cursor");
} else {
Log.d("NOT OUCH","Column " + test_column + " is in Cursor");
}
csr.close();
with output from the above being :-
09-20 13:25:35.163 4217-4217/? D/OUCH: Column Fred not in Cursor
09-20 13:25:35.164 4217-4217/? D/NOTĀ OUCH: Column aislename is in Cursor
I wouldn't mind the repetition if we could extract that code into a
separate method to be called for each column. But I don't see any
simple way to do it, because the columns can have different types. (We
could subclass CursorWrapper and implement getInt(), getString(), etc.
to do the checking we want, thus confining the repetition to a
specialized utility class. Is that the best we can do?)
I'm not really sure if the above code (isColumnInCursor method) would suit your requirements although I'm pretty sure it meets the single seperate method, at least for detecting the missing column condition.
Any other ideas? Any thoughts on the above ideas?
The above!?
Could someone suggest me a way on how to update my apps SQLite database when the content of the parsed JSON file is changed?
I am having a hard time finding the right solution, now I have to clear the cache before the app starts, but the end user obviously won't do that.
I asume changing the table version is not suitable for frequent updates (hourly).
Thanks!
As #KenWolf mentioned this documentation in the comments.
Update a Database:
When you need to modify a subset of your database values, use the update() method.
Updating the table combines the content values syntax of insert() with the where syntax of delete().
SQLiteDatabase db = mDbHelper.getWritableDatabase();
// New value for one column
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
// Which row to update, based on the title
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
String[] selectionArgs = { "MyTitle" };
int count = db.update(
FeedReaderDbHelper.FeedEntry.TABLE_NAME,
values,
selection,
selectionArgs);
Change your database version by incrementing by 1 from current value.
It will make possible of updation.
I have put an sqlite database in my assets folder and imported it onto the phone.
I created an object with multiple properties and when I create a list of that object and assign each property a value from a column of the table they get mixed up
Below is my code
public ArrayList<Exercise> getExercisesFromQuery(String Query) {
ArrayList<Exercise> ExerciseList = new ArrayList<Exercise>();
Cursor cursor = mDb.rawQuery(Query, null);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Exercise e = new Exercise();
e.setID(Integer.parseInt(cursor.getString(0)));
e.setName(cursor.getString(1));
e.setMuscle(cursor.getString(2));
e.setDescription(cursor.getString(3));
e.setFilepath(cursor.getString(4));
e.setSets(cursor.getString(5));
e.setReps(cursor.getString(6));
e.setEquipment(cursor.getString(7));
e.setPrimaryMuscle(cursor.getString(8));
e.setSecondaryMuscle(cursor.getString(9));
e.setDifficulty(cursor.getString(10));
// Adding contact to list
ExerciseList.add(e);
} while (cursor.moveToNext());
}
return ExerciseList;
}
The current problem is when I do object.getName it gives me the muscle and if I do object.getmuscle it is blank and there is no value but if I do object.getDescription it works fine.
It is not a problem with the database it works fine in any sqlite manager.
Any ideas as to what is wrong?
The reason why the columns are not being returned in the order you expect is not clear. They should come back in the order specified in your query or in the order they are on the table if you are doing SELECT *. However it is not really necessary to address that specific puzzle.
A more defensive and maintainable coding approach is to request each column's index from the cursor by using the getColumnIndexOrThrow method instead of hardcoding them. For example:
int ID_INDEX = cursor.getColumnIndexOrThrow("_id");
int NAME_INDEX = cursor.getColumnIndexOrThrow("name");
If the column doesn't exist you'll get an exception. If it does, you now have its index within the cursor which you can use in the calls to cursor.getString:
e.setID(Integer.parseInt(cursor.getString(ID_INDEX)));
e.setName(cursor.getString(NAME_INDEX));
So you no longer need to worry about what order the columns come back in and you won't need to change any hardcoded index values if your query changes in the future.
Make sure that the columns in the database are in the correct order - column Name should be the second column, column Muscle should be the third column.
I think it's kinda easy one but still I'm new to android programming so please have patience. I want to know how can I get the number of records (rows) in a specific table in my db. I need this so I can create a loop to go through every record and add each one of it to the specific Array and display it later on. This is the source:
db.openDataBase(); // open connection with db
Cursor c = db.getTitle(5); // loop here through db, right now I'm fetching only one record
startManagingCursor(c);
//adding areas to the list here
Area o1 = new Area();
o1.setOrderName(c.getString(1) + c.getString(2));
m_areas.add(o1);
db.close();
Does anyone can help me with this please? Thx in advance!
SELECT COUNT(*) FROM tablename
To get the number of rows in the cursor, use getCount.
To get the amount of total rows in a table, either use reinierposts solution, or do a select which select all rows in the table and get the count from the cursor. I'm guessing his solution is quicker though unless you actually need all the rows in the table.
Such a query would be:
SELECT * FROM footable;
You don't really need to get a count of how many first; instead, create a db.getTitles() function that returns all of the rows and returns a Cursor, then loop over the Cursor. Right now you probably have a query that looks something like SELECT ColumnA, ColumnB FROM Titles WHERE id = 5; just copy the function, remove the parameter and take off the WHERE clause so it looks like just SELECT ColumnA, ColumnB FROM Titles.
Then your code would look something like this:
db.openDataBase(); // open connection with db
Cursor c = db.getTitles();
startManagingCursor(c);
//adding areas to the list here
if (c != null && c.moveToFirst()) {
do {
Area o1 = new Area();
o1.setOrderName(c.getString(1) + c.getString(2));
m_areas.add(o1);
} while (c.next());
}
db.close();
We check if the function returned a cursor at all, then move to the beginning of the cursor and start looping, going to the next item each time through. For more information on the Cursor interface see the API here, or to learn more about database access and related design practices better in general I suggest going through the Notepad tutorial.