IllegalStateException on Cursor - java

I am getting this error when trying to read from the SQLite DB
IllegalStateException: Couldn't read row 0, col -1 from CursorWindow. Make sure the Cursor is initialized correctly before accessing data from it.
DbHelper dbHelper = new DbHelper(this);
SQLiteDatabase database = dbHelper.getWritableDatabase();
Cursor cursor = dbHelper.readNumber(database);
String ItemId;
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
//getting the ERROR here
ItemId = cursor.getString(cursor.getColumnIndex(DbContract.ITEMID));
number = cursor.getString(cursor.getColumnIndex(DbContract.PHONE_NUMBER));
callType = cursor.getString(cursor.getColumnIndex(DbContract.CALL_TYPE));
callDate = cursor.getString(cursor.getColumnIndex(DbContract.CALL_DATE));
callDuration = cursor.getString(cursor.getColumnIndex(DbContract.CALL_DURATION));
arrayList.add(new PhNumber(ItemId, number, callType, callDate, callDuration));
if (debug) {
Log.i(TAG, "DATA FOUND IN DB:\n" + "\t ID: " + ItemId + ", Number: " + number + "\n");
}
}
cursor.close();
dbHelper.close();
result = true;
if (debug) {
Log.d(TAG, " Number of items in DB: " + arrayList.size());
}
}
readNumber
public Cursor readNumber(SQLiteDatabase database) {
String[] projections = {"id", DbContract.PHONE_NUMBER};
return (database.query(DbContract.TABLE_NAME, projections, null, null, null, null, null));
}
This is my DB
private static final String CREATE = "create table " + DbContract.TABLE_NAME +
"(id integer primary key autoincrement,"
+ DbContract.ITEMID + " text, "
+ DbContract.PHONE_NUMBER + " text, "
+ DbContract.CALL_TYPE + " text, "
+ DbContract.CALL_DATE + " text, "
+ DbContract.CALL_DURATION + " text, "
+ DbContract.SYNC_STATUS + " text)";

In your projection, created by readNumber call, you have only the id and PHONE_NUMBER columns returned. Probably DbContract.ITEMID is not equal to id and when trying to lookup the DbContract.ITEMID in the cursor it is not found. To fix this you need to use ITEMID in readNumber method, something like:
public Cursor readNumber(SQLiteDatabase database) {
String[] projections = {DbContract.ITEMID, DbContract.PHONE_NUMBER};
return (database.query(DbContract.TABLE_NAME, projections, null, null, null, null, null));
}
Another issue is that you are trying to access other fields too, like: CALL_TYPE, CALL_DATE, etc.
So, in order to fix the issue you either:
Do not try to retrieve the fields that are not part of the result.
Add the needed fields in the projection too.

Found the issue:
I was trying to access the columns that were not added in the projects in readNumber method adding those projections solved the issue.
readNumber
public Cursor readNumber(SQLiteDatabase database) {
String[] projections = {
DbContract.ITEMID,
DbContract.PHONE_NUMBER,
DbContract.CALL_TYPE,
DbContract.CALL_DATE,
DbContract.CALL_DURATION};
return (database.query(DbContract.TABLE_NAME, projections, null, null, null, null, null));
}

Related

Android : Calculate sum and group by month (SQLite)

Is there are any option to select amount, group them by month and calculate sum. I tried to get total sum of each month and pass it to ArrayList.
Example of data:
Amount Date
230 04/03/19
500 05/03/19
400 04/04/19
600 06/04/19
100 04/03/19
... ...
My code structure
private String CREATE_BILLS_TABLE = "CREATE TABLE " + TABLE_BILLS + "("
+ COLUMN_BILL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ COLUMN_BILL_USER_ID + " INTEGER,"
+ COLUMN_DESCRIPTION + " TEXT,"
+ COLUMN_AMOUNT + " INTEGER,"
+ COLUMN_DATE_STRING + " TEXT,"
+ COLUMN_COMPANY_NAME + " TEXT,"
+ COLUMN_CATEGORY + " TEXT,"
+ " FOREIGN KEY ("+COLUMN_BILL_USER_ID+") REFERENCES "+TABLE_USER+"("+COLUMN_USER_ID+"));";
public ArrayList<Bills> getDateByUserID(int userID){
SQLiteDatabase db = this.getReadableDatabase();
// sorting orders
ArrayList<Bills> listBillsDates = new ArrayList<Bills>();
Cursor cursor = db.query(TABLE_BILLS, new String[] { COLUMN_BILL_ID,
COLUMN_BILL_USER_ID, COLUMN_DESCRIPTION, COLUMN_AMOUNT, COLUMN_DATE_STRING, COLUMN_COMPANY_NAME, COLUMN_CATEGORY}, COLUMN_BILL_USER_ID + "=?",
new String[] { String.valueOf(userID) }, COLUMN_DATE_STRING, null, null, null);
if (cursor.moveToFirst()) {
do {
Bills bills = new Bills();
bills.setAmount(cursor.getInt(cursor.getColumnIndex(COLUMN_AMOUNT)));
bills.setDateString(cursor.getString(cursor.getColumnIndex(COLUMN_DATE_STRING)));
// Adding record to list
listBillsDates.add(bills);
} while (cursor.moveToNext());
}
cursor.close();
db.close();
// return category list
return listBillsDates;
}
I believe that a query based upon :-
SELECT sum(COLUMN_AMOUNT) AS Monthly_Total,substr(COLUMN_DATE_STRING,4) AS Month_and_Year
FROM TABLE_BILLS
WHERE COLUMN_BILL_USER_ID = 1
GROUP BY substr(COLUMN_DATE_STRING,4)
ORDER BY substr(COLUMN_DATE_STRING,7,2)||substr(COLUMN_DATE_STRING,4,2)
;
Note that other columns values would be arbritary results and as such cannot really be relied upon (fine if the data is always the same). Hence they have not been included.
Will produce the results that you want :-
e.g.
Using the following, to test the SQL :-
DROP TABLE IF EXISTS TABLE_BILLS;
CREATE TABLE IF NOT EXISTS TABLE_BILLS (
COLUMN_BILL_ID INTEGER PRIMARY KEY AUTOINCREMENT,
COLUMN_BILL_USER_ID INTEGER,
COLUMN_DESCRIPTION TEXT,
COLUMN_AMOUNT INTEGER,
COLUMN_DATE_STRING TEXT,
COLUMN_COMPANY_NAME TEXT,
COLUMN_CATEGORY TEXT)
;
-- Add the Testing data
INSERT INTO TABLE_BILLS (
COLUMN_BILL_USER_ID, COLUMN_DESCRIPTION, COLUMN_AMOUNT, COLUMN_DATE_STRING, COLUMN_COMPANY_NAME,COLUMN_CATEGORY)
VALUES
(1,'blah',230,'04/03/19','cmpny','category')
,(1,'blah',500,'05/03/19','cmpny','category')
,(1,'blah',400,'04/04/19','cmpny','category')
,(1,'blah',600,'06/04/19','cmpny','category')
,(1,'blah',100,'04/03/19','cmpny','category')
-- Extra data for another id to check exclusion
,(2,'blah',230,'04/03/19','cmpny','category')
,(2,'blah',500,'05/03/19','cmpny','category')
,(2,'blah',400,'04/04/19','cmpny','category')
,(2,'blah',600,'06/04/19','cmpny','category')
,(2,'blah',100,'04/03/19','cmpny','category')
;
SELECT sum(COLUMN_AMOUNT) AS Monthly_Total,substr(COLUMN_DATE_STRING,4) AS Month_and_Year
FROM TABLE_BILLS
WHERE COLUMN_BILL_USER_ID = 1
GROUP BY substr(COLUMN_DATE_STRING,4)
ORDER BY substr(COLUMN_DATE_STRING,7,2)||substr(COLUMN_DATE_STRING,4,2)
;
Results id :-
The above can then be converted for use by the SQLiteDatabase query method. So your method could be something like :-
public ArrayList<Bills> getDateByUserID(int userID) {
SQLiteDatabase db = this.getReadableDatabase();
String tmpcol_monthly_total = "Monthly_Total";
String tmpcol_month_year = "Month_and_Year";
String[] columns = new String[]{
"sum(" + COLUMN_AMOUNT + ") AS " + tmpcol_monthly_total,
"substr(" + COLUMN_DATE_STRING + ",4) AS " + tmpcol_month_year
};
String whereclause = COLUMN_BILL_USER_ID + "=?";
String[] whereargs = new String[]{String.valueOf(userID)};
String groupbyclause = "substr(" + COLUMN_DATE_STRING + ",4)";
String orderbyclause = "substr(" + COLUMN_DATE_STRING + ",7,2)||substr(" + COLUMN_DATE_STRING + ",4,2)";
ArrayList<Bills> listBillsDates = new ArrayList<Bills>();
Cursor cursor = db.query(TABLE_BILLS, columns, whereclause,
whereargs, groupbyclause, null, orderbyclause, null);
if (cursor.moveToFirst()) {
do {
Bills bills = new Bills();
bills.setAmount(cursor.getInt(cursor.getColumnIndex(tmpcol_monthly_total)));
bills.setDateString(cursor.getString(cursor.getColumnIndex(tmpcol_month_year))); //<<<<<<<<<< NOTE data is MM/YY (otherwise which date to use? considering result will be arbrirtaryy)
// Adding record to list
listBillsDates.add(bills);
} while (cursor.moveToNext());
}
cursor.close();
db.close();
// return category list
return listBillsDates;
}
The above has been tested and run and using the following code :-
ArrayList<Bills> myMonthlyTotals = mDBHelper.getDateByUserID(1);
Log.d("BILLSCOUNT","The number of bills extracted was " + String.valueOf(myMonthlyTotals.size()));
for (Bills b: myMonthlyTotals) {
Log.d("MONTHYLTOTAL","Monthly total for " + b.getDateString() + " was " + String.valueOf(b.getAmount()));
}
In an activity, resulted in the following in the log
:-
04-14 11:58:25.876 16653-16653/? D/BILLSCOUNT: The number of bills extracted was 2
04-14 11:58:25.877 16653-16653/? D/MONTHYLTOTAL: Monthly total for 03/19 was 830
04-14 11:58:25.877 16653-16653/? D/MONTHYLTOTAL: Monthly total for 04/19 was 1000
Please consider the comments in regard to values from non-aggreagted columns be arbitrary values. As per :-
Each non-aggregate expression in the result-set is evaluated once for an arbitrarily selected row of the dataset. The same arbitrarily selected row is used for each non-aggregate expression. Or, if the dataset contains zero rows, then each non-aggregate expression is evaluated against a row consisting entirely of NULL values. SELECT - 3. Generation of the set of result rows.
As per the comments, using recognised date formats can make the underlying SQL simpler and likely more efficient.

Android SQLITE DB update table not working

contactid = 123;
SYNC_SUCCESS = 1;
db.updateSyncStatus(contactid, SYNC_SUCCESS);
I have tried the 3 possible ways to update the table in SQLite DB. But its not working. INSERT and DELETE process are working good. Only I am facing problem in the UPDATE. Did I missed anything?
public void updateSyncStatus(String contactid, int syncSuccess) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues CV = new ContentValues();
CV.put(CONTACTS_SYNC_STATUS, syncSuccess);
try {
// db.update(TABLE_CONTACTS, CV, CONTACTS_CONTACTID + "='" + contactid + "'", null); // Tried, Not working
// db.update(TABLE_CONTACTS, CV, CONTACTS_CONTACTID +" = ?", new String[] {contactid}); // Tried, Not Working
db.update(TABLE_CONTACTS, CV, CONTACTS_CONTACTID + " = ?", new String[]{contactid});
}
catch (Exception e){
String error = e.getMessage().toString();
Log.e(TAG, "UpdateError: " + error);
}
db.close();
}
Table Structure:
String CREATE_CONTACTS_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_CONTACTS + "("
+ CONTACTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
+ CONTACTS_NUMBER + " VARCHAR,"
+ CONTACTS_CONTACTID + " VARCHAR,"
+ CONTACTS_SYNC_STATUS + " TINYINT DEFAULT 0" + ")";
db.execSQL(CREATE_CONTACTS_TABLE);
Actually the problem is not with the update query. The problem is , before executing the updateSyncStatus method, next statement ran and I am getting the output before updating the rows. So I have used the Handler to wait for 10 seconds before showing the output.

Cursor returns with no result after SQLite query (passes unit test but fails at run time) -- android

i have this method:
public Note getNoteById(long id) {
Cursor cursor = database.query(Config.NOTES_TABLE, Config.NOTES_COLUMNS, Config.ROW_ID+"="+id, null, null, null, Config.ROW_ID + " DESC");
//no match found
if(cursor.getCount() == 0)
return null;
cursor.moveToFirst();
Note result = new Note();
result.setId(cursor.getLong(cursor.getColumnIndex(Config.ROW_ID)));
result.setTitle(cursor.getString(cursor.getColumnIndex(Config.TITLE)));
result.setText(cursor.getString(cursor.getColumnIndex(Config.TEXT)));
result.setViewOrder(cursor.getLong(cursor.getColumnIndex(Config.VIEW_ORDER)));
result.setCreated(cursor.getString(cursor.getColumnIndex(Config.CREATED)));
result.createTagsFromString(cursor.getString(cursor.getColumnIndex(Config.TAGS)));
return result;
}
My problem is in the first few lines. The cursor at the beginning always returns with a count of 0 therefore making the function always returns null -- ONLY AT RUNTIME! -- i have written a test case for this method and it passes so i cant figure out why it would only fail at runtime?
heres the relevant part of the test case. i basically insert new data to the table and try to get it back by its id and compare attributes to confirm its the same:
#Test
public void testGetNoteByIdMatch(){
testTable.load();
Assert.assertEquals(0, testTable.getAllNotes().getCount());
testTable.newEntry(note1);
testTable.newEntry(note2);
Cursor cursor = testTable.getAllNotes();
Assert.assertEquals(2, testTable.getAllNotes().getCount());
cursor.moveToFirst();
Note result = new Note();
result.setId(cursor.getLong(cursor.getColumnIndex(Config.ROW_ID)));
result.setTitle(cursor.getString(cursor.getColumnIndex(Config.TITLE)));
result.setText(cursor.getString(cursor.getColumnIndex(Config.TEXT)));
Note checker = testTable.getNoteById(result.getId());
Assert.assertEquals(result.getId(), checker.getId());
Assert.assertEquals(result.getTitle(), checker.getTitle());
//move and try another search
cursor.moveToNext();
result = new Note();
result.setId(cursor.getLong(cursor.getColumnIndex(Config.ROW_ID)));
result.setTitle(cursor.getString(cursor.getColumnIndex(Config.TITLE)));
result.setText(cursor.getString(cursor.getColumnIndex(Config.TEXT)));
checker = testTable.getNoteById(result.getId());
Assert.assertEquals(result.getId(), checker.getId());
Assert.assertEquals(result.getTitle(), checker.getTitle());
}
this is the table structure in the onCreate method for the database:
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + Config.NOTES_TABLE + " (" +
Config.ROW_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
Config.TITLE + " TEXT NOT NULL, " +
Config.TEXT + " TEXT NOT NULL, " +
Config.CREATED + " TEXT NOT NULL, " +
Config.VIEW_ORDER + " INTEGER NOT NULL, " +
Config.TAGS + " TEXT NOT NULL)"
);
}
Your test checks that the values can be read out in the same order they were inserted, but does not actually verify the data is going in as expected. It's possible that the result and checker objects both have null values for all properties. Verify that the object properties have the expected values once read out of the db.

android.database.sqlite.SQLiteException: table has no such column

So I guess it is a very common issue, searching the web I found that I am not the only one who faced a such issue and yes I know that there is a question with almost the same title, however that did not help to solve the issue I am facing ... so let's start from the beginning
I am simply trying to insert into a table that I created.
This table has three columns: "id", "name", "value", and was created as following
public static final String TABLE_NAME = "cookie";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_VALUE = "val";
private static final String DATABASE_NAME = "commments.db";
private static final int DATABASE_VERSION = 2;
// Database creation sql statement
private static final String DATABASE_CREATE = "create table "
+ TABLE_NAME + "("
+ COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_VALUE + "text not null, "
+ COLUMN_NAME + " text not null"
+ ");";
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
Now I am trying to insert into this table as following
ContentValues values = new ContentValues();
values.put(MySQLiteHelper.COLUMN_NAME, "username");
values.put(MySQLiteHelper.COLUMN_VALUE, username);
long insertId = database.insert(MySQLiteHelper.TABLE_NAME, null, values);
Cursor cursor = database.query(MySQLiteHelper.TABLE_NAME, allColumns, MySQLiteHelper.COLUMN_ID + " = " + insertId, null, null, null, null);
cursor.moveToFirst();
nameValuePair newComment = cursorToNameValuePair(cursor);
cursor.close();
However I am getting this error
table cookie has no column named val
I searched for similar issues online, most of the solution where talking about a change happened to the database so I need to either
1- Un-install the application before trying to run in debugging mode again
2- update the database version from 1 to 3
However that did not help .. So looking forward for your solutions :)
Problem is here
+ COLUMN_VALUE + "text not null, "
into DATABASE_CREATE String. You missed space between column name and column type.
It should be
+ COLUMN_VALUE + " text not null, "

SQLitedatabase query

I have three columns in my database id ,message and message status and I only want to select only those rows from the list whose message status is 'r' and want to return the cursor from query for only id and message. I am new to databases,Please help.
My current code which is selecting all the rows is:
private String[] allColumns = { MySQLiteHelper.COLUMN_ID,MySQLiteHelper.COLUMN_MESSAGE };
public List<Message> getAllMessages() {
List<Message> message = new ArrayList<Message>();
Cursor cursor = database.query(MySQLiteHelper.TABLE_NAME,allColumns, null, null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Message message1 = cursorToMessage(cursor);
message.add(message1);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return message;
}
SQLiteDatabase database = this.getReadableDatabase();
String queryz = "SELECT " + COLUMN_ID + "," + COLUMN_MESSAGE + " FROM " + TABLE_NAME + " WHERE " + COLUMN_MESSAGE_STATUS + "= 'r'";
Cursor c = database.rawQuery(queryz, null);
You need to pass a Where clause yo your query. It is the 4th parameter of the query(). It takes a String, and you should not include the Sqlite3 keyword WHERE (Android does that for you). The clause can be structured like MySQLiteHelper.COLUMN_MESSAGE+"="+"r"
Try this code,
public static final String KEY_ROWID = "row";
public static final String KEY_NAME = "name";
public Cursor fetchNamesByConstraint(String filter) {
Cursor cursor = mDb.query(true, DATABASE_NAMES_TABLE, null, "row LIKE '%" + filter + "%' or name LIKE '%" + filter + "%'",null, null, null, null);
}

Categories