I have the following code in a bigger project:
final class DBlifetimeStatisticsHandler{ //implements DBvalueHandler<Cyclist, Double>{
private final String TAG = getClass().getName();
private static final boolean debug = true;
private final DBminMaxAvgHandler dbMinMaxAvgHandler = new DBminMaxAvgHandler();
// table name
private static final String TABLE_LIFETIME_STATISTICS = "lifetime_statistics";
// column names
private static final String KEY_LIFETIME_STATISTICS_ID = "lifetime_statistics_id";
private static final String KEY_MIN_MAX_AVG = "min_max_avg";
// table create statement
private static final String CREATE_TABLE = "CREATE TABLE "
+ TABLE_LIFETIME_STATISTICS + "("
+ KEY_LIFETIME_STATISTICS_ID + " LONG PRIMARY KEY NOT NULL,"
+ KEY_MIN_MAX_AVG + " LONG"
+ ")";
public void onCreateTable(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}
public void onUpgrade(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_LIFETIME_STATISTICS);
onCreateTable(db);
}
public long addValue(SQLiteDatabase db, Statistics Statistics ) {
ContentValues values = new ContentValues();
long ID = getLatestID(db)+1;
values.put(KEY_STATISTICS_ID, ID);
... //not important to the question
}
private long getLatestID(SQLiteDatabase db){
String selectQuery = "SELECT MAX(" + KEY_STATISTICS_ID +") FROM " + TABLE_STATISTICS;
Cursor c = db.rawQuery(selectQuery, null);
c.moveToFirst();
int id = 0;
Log.e("count", String.valueOf(c.getCount()));
if (c.moveToFirst()){
...
}
return id;
}
}
After I updated the table it is created again. So when I try to add a new value I had problems cause it always jumped into the if clause because c.moveToFirst() always returned true.
So I tried to tried to check if c.getCount() would return true but sadly it does always return 1. So the question is: Why would it return 1 on an empty table? (I do use Questoid SQLite Browser and the table is really empty)
You use aggregate function MAX, so read documentation:
There are two types of simple SELECT statement - aggregate and non-aggregate queries. A simple SELECT statement is an aggregate query if it contains either a GROUP BY clause or one or more aggregate functions in the result-set.
An aggregate query without a GROUP BY clause always returns exactly one row of data, even if there are zero rows of input data.
It might be some kind of a buggy behavior when using MAX. Check this link too Android database (SQLite) returns a non-empty cursor from an empty table
this is my solution
public Boolean isNotEmpty(){
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_STATISTICS, null);
while (cursor.moveToNext() ) {
return true;
}
return false;
}
You are getting a result with one row in your Cursor because that is what you requested.
The result is a single column called MAX with a value that will be the max id of all the rows in your table. In your case of an empty table, this value is null.
I am using group by to resolve this. Please check my example :
SELECT COUNT(*) FROM " + TABLE_NAME + " WHERE isSynced=0 group by isSynced
I resolve this probme this way:
SELECT COUNT(*) AS numero, MAX(tagua_lagps) as tmp_max_lagps, MAX(tagua_logps) as tmp_max_logps, MIN(tagua_lagps) as tmp_min_lagps, MIN(tagua_logps) as tmp_min_logps FROM TAB_AGUA
On empty table, c.getCount(); gives 1 but values are NULL. But numero (c.getString(c.getColumnIndex("numero")) has a value of 0.
So rather than checking c.getCount() you must check the result of count(*).
Related
I want to fetch phone number linked to particular email in the database. I am not able to find the query for it or how
public String getContactNumber(String email){
SQLiteDatabase db = this.getReadableDatabase();
String query = "SELECT " + COLUMN_USER_MOBILE_NUMBER + " FROM " + TABLE_USER + " WHERE " + email + " = " + COLUMN_USER_EMAIL;
Cursor cursor = db.rawQuery(query,null);
//What to put here to extract the data.
String contact = cursor.getString(get);
cursor.close();
return contact;
}
to extract the data. Completely a beginner
Try this ..
public List<String> getMyItemsD(String emailData) {
List<String> stringList = new ArrayList<>();
SQLiteDatabase db = this.getReadableDatabase();
String selectQuery = "SELECT COLUMN_USER_MOBILE_NUMBER FROM " + USER_TABLE_NAME + " WHERE email= " + emailData;
Cursor c = db.rawQuery(selectQuery, null);
if (c != null) {
c.moveToFirst();
while (c.isAfterLast() == false) {
String name = (c.getString(c.getColumnIndex("Item_Name")));
stringList.add(name);
c.moveToNext();
}
}
return stringList;
}
public String getContactNumber(String email){
String contact = "";
SQLiteDatabase db = this.getReadableDatabase();
String query = "SELECT " + COLUMN_USER_MOBILE_NUMBER + " FROM " + TABLE_USER + " WHERE " + email + " = " + COLUMN_USER_EMAIL;
Cursor cursor = db.rawQuery(query,null);
if(cursor.getCount()>0) {
cursor.moveToNext();
contact = cursor.getString(cursor.getColumnIndex(COLUMN_USER_MOBILE_NUMBER));
}
//What to put here to extract the data.
cursor.close();
return contact;
}
From this method you get phone number value of that email which you pass any other method easily.
I'd suggest the following :-
public String getContactNumber(String email){
String contact = "NO CONTACT FOUND"; //<<<<<<<<<< Default in case no row is found.
SQLiteDatabase db = this.getWritableDatabase(); //<<<<<<<<<< Generally getReadable gets a writable database
String[] columns_to_get = new String[]{COLUMN_USER_MOBILE_NUMBER};
String whereclause = COLUMN_USER_EMAIL + "=?";
String[] whereargs = new String[]{email};
Cursor cursor = db.query(TABLE_USER,columns_to_get,whereclause,whereargs,null,null,null);
//What to put here to extract the data.
if (cursor.moveToFirst()) {
contact = csr.getString(csr.getColumnIndex(COLUMN_USER_MOBILE_NUMBER));
}
cursor.close();
return contact;
}
The above does assumes that there will only be 1 row per email (which is most likely).
Explanations
A default value is set so that you can easily tell if an invalid/non-existent email is passed (you'd check the return value if need be (might be easier to simply have "" and check the length as a check)).
getReadableDatabase has been replaced with getWritableDatabase as unless there are issues with the database a writable database will be returned, as per :-
Create and/or open a database. This will be the same object returned
by getWritableDatabase() unless some problem, such as a full disk,
requires the database to be opened read-only. In that case, a
read-only database object will be returned. If the problem is fixed, a
future call to getWritableDatabase() may succeed, in which case the
read-only database object will be closed and the read/write object
will be returned in the future.
getReadableDatabase
Note no real problem either way;
The recommended query method has been used instead of the rawQuery method. This has distinct advantages, it builds the underlying SQL and also offers protection against SQL injection (just in case the email passed is input by a user).
this version of the method takes 7 parameters :-
The table name as a string
The columns to be extracted as an array of Strings (aka String array). null can be all columns.
The where clause less the WHERE keyword with ?'s to represent arguments (see next). null if no WHERE clause.
The arguments to be applied (replace ?'s 1 for 1) as a String array. null if none or no WHERE clause.
The GROUP BY clause, less the GROUP BY keywords. null if no GROUP BY clause.
The HAVING clause, less the HAVING keyword. null if no HAVING clause.
The ORDER BY clause, less the ORDER BY keywords. null if no ORDER BY clause.
SQLiteDatabase - query
- Note there are 4 query methods (see link for the subtle difference, I believe this is the most commonly used)
The data extraction is the new code. When a Cursor is returned it is at a position BEFORE THE FIRST ROW, so you need to move to a valid row. So the moveToFirst* method is suitable (note that if a move cannot be made by a move method that it will return false, hence how you can say if (cursor.moveToFirst())). The data is then extracted from the appropriate column use the **getString method, which takes an int as an argumnet for the column offset (0 in this case). However, using hard coded values can lead to issues so the getColumnIndex method is used to get the offset according to the column name (-1 is returned if the named column is not in the Cursor).
I am trying to create a fitness app where the database saves a username and password.
then enters their details that saves to a second table. This is my dbHelper.
The error im getting is that my "Username Column does not exist"
But when i go and look at my tables using db browser for sqlite
it shows my tables created and data in my tables
UPDATE : I created 1 table to store all my data and now its not picking up still im getting "not set" from my display method
updated table
// Register table
public static final String COL_1 = "ID";
public static final String COL_2 = "Username";
public static final String COL_3 = "Password";
public static final String COL_4 = "Weight";
public static final String COL_5 = "Height";
public static final String COL_6 = "TargetWeight";
public static final String COL_7 = "TargetSteps";
display method
public String DisplayData(String username,String column)
{
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM "+ TABLE_NAME +" WHERE Username =?",new String[]{username});
if(cursor.moveToFirst()){
return cursor.getString(cursor.getColumnIndexOrThrow(column));
}else{
return "Not set";
}
}
Usage
public void setData() {
db = new dbHelper(this);
try {
userWeight.setText(db.DisplayData(Username, dbHelper.COL_4));
userHeight.setText(db.DisplayData(Username, dbHelper.COL_5));
userTargetWeight.setText(db.DisplayData(Username, dbHelper.COL_6));
userTargetSteps.setText(db.DisplayData(Username, dbHelper.COL_7));
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
TABLE_NAME1 has no Username column. update your method as follow
public String DisplayData(String username, String column) {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM "+ TABLE_NAME +" WHERE " + column + " = ? ", new String[]{username});
if (cursor != null && cursor.moveToFirst()){
return cursor.getString(cursor.getColumnIndexOrThrow(column));
} else {
return "Not set";
}
}
and use it like this
.DisplayData("admin", "Username");
Your issue, assuming that value of the 2nd argument of the Displaydata method is Username is that you are querying TABLE_NAME1 (profile_data) table, which doesn't have a column named Username.
Instead I believe you want to be querying the TABLE_NAME (register_table table) table so change :-
Cursor cursor = db.rawQuery("SELECT * FROM "+ TABLE_NAME1 +" WHERE Username = 'admin' ",null);
to :-
Cursor cursor = db.rawQuery("SELECT * FROM "+ TABLE_NAME +" WHERE Username = 'admin' ",null);
Additional re comment :-
Im not longer getting an error but its still not displaying the
correcting infomation . Im getting "Not set " from my displayData
method. from my if ELSE
Assuming that you have added data and getting the above then it is likely that Username does not equate to a row in the table. Try using the following version of DisplayData to debug :-
// Note changed to use recommended convenience query method
// Note closes cursor thus uses intermediate variable (rv) to allow close
public String DisplayData(String username,String column)
{
String rv = "Not set";
SQLiteDatabase db = this.getReadableDatabase();
//Cursor cursor = db.rawQuery("SELECT * FROM "+ TABLE_NAME +" WHERE Username =?",new String[]{username}); //<<<<< replaced
String whereclause = COL_2 + "=?";
String[] whereargs = new String[]{username};
Cursor cursor = db.query(TABLE_NAME,null,whereclause,whereargs,null,null,null);
//<<<<<<<<<< FOLLOWING CODE ADDED TO LOG DEBUG INFO >>>>>>>>>>
Log.d("DISPLAYDATAINFO","Display was called with Username as:- " +
username +
" for Column:- " +
column +
". The Cursor contains " +
String.valueOf(cursor.getCount()) +
" ."
);
//<<<<<<<<<< END OF ADDED DEBUG CODE >>>>>>>>>>
if(cursor.moveToFirst()){
rv = cursor.getString(cursor.getColumnIndexOrThrow(column));
}
cursor.close(); //<<<< SHOULD ALWAYS CLOSE CURSOR WHEN DONE WITH IT
return rv;
}
This should produce output in the log along the lines of :-
05-18 23:08:25.429 2926-2926/fitness.fitness D/DISPLAYDATAINFO: Display was called with Username as:- Fred for Column:- Weight. The Cursor contains 5 .
Display was called with Username as:- Fred for Column:- Height. The Cursor contains 5 .
Display was called with Username as:- Fred for Column:- TargetWeight. The Cursor contains 5 .
Display was called with Username as:- Fred for Column:- TargetSteps. The Cursor contains 5 .
Note 5 because when testing new data is inserted each run so the above indicates the 5th run.
Or in the case of nothing being found (your current issue) something like :-
05-18 23:11:40.342 2926-2926/fitness.fitness D/DISPLAYDATAINFO: Display was called with Username as:- Tom for Column:- Weight. The Cursor contains 0 .
Display was called with Username as:- Tom for Column:- Height. The Cursor contains 0 .
Display was called with Username as:- Tom for Column:- TargetWeight. The Cursor contains 0 .
Display was called with Username as:- Tom for Column:- TargetSteps. The Cursor contains 0 .
i.e. Cursor contains 0 = no rows exist for the given username (Tom in this case).
Check if the Username is as expected (note case of letters must match, in the above a row for tom exists but not for Tom hence 0 count for the cursor).
Column retrieval appears to be correct, However, still check that the columns in the output are as expected (can't see that they would not be).
I am developing an application where the user inputs title and the date. I want to prevent the duplicated titles being inputted on the same day in to database. I am checking if the title exists on the selected date. However my query seems not to work and i don't know why, the application just crashes.Is this query correct? Can someone help?
public boolean checkExist(String title, String date) {
SQLiteDatabase db = this.getWritableDatabase();
Cursor c = db.rawQuery("SELECT * FROM "+TABLE_NAME+" WHERE "+TITLE+"=?" +"AND" + DATE+"=?", new String[] {title,date});
boolean exists = c.moveToFirst();
c.close();
return exists;
}
One issue that you have is that c.moveToFirst will always fail if a match does not exist as you are trying to move to a row in an empty cursor.
The resolution is to not use c.moveToFirst and instead get the count of the rows and then set the return value accordingly.
e.g.
public boolean checkExist(String title, String date) {
SQLiteDatabase db = this.getWritableDatabase();
Cursor c = db.rawQuery("SELECT * FROM "+TABLE_NAME+" WHERE "+TITLE+"=?" +"AND" + DATE+"=?", new String[] {title,date});
boolean exists = c.getCount() > 0;
c.close();
return exists;
}
The second issue is that the query itself is wrong as you do not have spaces either side of the AND keyword. That is instead of
Cursor c = db.rawQuery("SELECT * FROM "+TABLE_NAME+" WHERE "+TITLE+"=?" +"AND" + DATE+"=?", new String[] {title,date});
You should have
Cursor c = db.rawQuery("SELECT * FROM "+TABLE_NAME+" WHERE "+TITLE+"=?" +" AND " + DATE+"=?", new String[] {title,date});
Personally, I setup constants for SQL keywords that include the space and then use these. So I'd have something along the lines of +TITLE+"=?" + SQLAND + DATE+"=?". Where SQLAND would be defined along the lines of String SQLAND=" AND ";
PS look at Cricket_007's answer, the code is neater/better it's easier to read.
Your spacing is off. TITLE+"=?" +"AND" + DATE becomes TITLE=?ANDDATE=?
I would suggest this. See DatabaseUtils.queryNumEntries
public boolean checkExist(String title, String date) {
SQLiteDatabase db = getReadableDatabase();
String[] args = new String[] {title,date};
String filter = String.format("%s=? AND %s=?", TITLE, DATE);
return DatabaseUtils.queryNumEntries(db, TABLE_NAME, filter, args) > 0;
}
you should be using c.getCount() instead of c.moveToFirst()
if the value is greater than 0, then it exists
While working with SQLiteCursor in Android I came to know that the getColumnIndex() is behaving case sensitive for example:
Example:
Column Name in DB was: Rules
cursor.getColumnIndex("Rules") //works fine
cursor.getColumnIndex("rules") //throws error, see the error detail
The documentation says nothing about that, for detail please see this.
LogCat says:
java.lang.IllegalStateException: Couldn't read row 0, col -1 from
CursorWindow. Make sure the Cursor is initialized correctly before
accessing data from it
I am confused by this behavior of SQLiteCursor, can someone help me that this is true OR I am doing something wrong? I can provide the code if required.
Thanks.
getColumnIndex() is case sensitive:
Column Name in DB was: Rules
cursor.getColumnIndex("Rules") //workes fine
cursor.getColumnIndex("rules") //throws error, see the error detail
The best and recommended approach using SQLite is that you declare all your table name and column name static, final and class level.. for example:
// write table name
public static final String TABLE_MESSAGE = "messages";
// and column name accordingly
public static final String COLUMN_ID = "_id";
public static final String COLUMN_MESSAGE = "message";
so the benefit of this approach is you don't need to remember the spelling and case etc of the table and column names.
when you access any table or column you simply use these static variables for example:
// TABLE creation sql statement
private static final String TABLE_CREATE = "create table "
+ TABLE_MESSAGE + "( " + COLUMN_ID
+ " integer primary key autoincrement, " + COLUMN_MESSAGE
+ " text not null);";
while querying:
database.query(TABLE_MESSAGE, new String[]{COLUMN_ID,COLUMN_MESSAGE}, null, null, null, null, null);
or it may be used in Cursor
int index = cursor.getColumnIndex(COLUMN_MESSAGE);
this will help you to avoid such conflicts of case sensitivity and spelling mistakes. :)
Another way would be to Query the database itself for the correct name by using PRAGMA table_info, So I wrote a method for just that:
public class database {
private SQLiteDatabase mainDB = null;
private boolean CreateOrOpenDB() {
try {
if (mainDB == null || !mainDB.isOpen()) {
mainDB = Context.openOrCreateDatabase("mainDB", SQLiteDatabase.CREATE_IF_NECESSARY, null);
}
} catch (SQLiteException e) {
return false;
}
return true;
}
private String GetTrueColumnName(String TableName, String column) {
String TrueColName = "";
if (CreateOrOpenDB()) {
try {
Cursor c = mainDB.rawQuery("PRAGMA table_info(" + TableName + ");", null);
if (c != null) {
if (c.moveToFirst()) {
do {
String dbcolumn = c.getString(c.getColumnIndex("name"));
if (column.toLowerCase().equals(dbcolumn.toLowerCase())) {
TrueColName = dbcolumn;
break;
}
} while (c.moveToNext());
}
c.close();
}
mainDB.close();
} catch (Exception e) {
}
}
return TrueColName;
}
}
then all you need to call is:
String CorrectName = GetTrueColumnName(TableName, "RuLeS");
and yes, I know it will be hard on the database. But it works and is stable
return readableDatabase
.query(
ProductosContract.ProductosEntry.TABLE_NAME,
ProductosContract.ProductosEntry.ALL_COLUMNS_NAME_ALIAS, null, null, null, null, null
)
You can specify the columns to retrieve, in that parameter add column name alias to lower case like (Kotlin):
arrayOf("name as 'name'")
So you will get always the lowercase one. Use the lower case or the one you prefer, it will work.
Using the typical SQLiteDatabase object in Android's API, what can I do to get the next AUTO_INCREMENT value of a particular column (ie. id) without affecting the value itself. Is there a method for that? Or what query should I execute to get that result. Keep in mind that SQLiteDatabase.query() returns a Cursor object, so I'm not too sure how to deal with that directly if I just want to get a value out of it.
You're right. The first answer (still below) only works without an AUTOINCREMENT for id. With AUTOINCREMENT, the values are stored in a separate table and used for the increment. Here's an example of finding the value:
public void printAutoIncrements(){
String query = "SELECT * FROM SQLITE_SEQUENCE";
Cursor cursor = mDb.rawQuery(query, null);
if (cursor.moveToFirst()){
do{
System.out.println("tableName: " +cursor.getString(cursor.getColumnIndex("name")));
System.out.println("autoInc: " + cursor.getString(cursor.getColumnIndex("seq")));
}while (cursor.moveToNext());
}
cursor.close();
}
See: http://www.sqlite.org/autoinc.html
First Answer:
You can query for the max of the _id column, such as:
String query = "SELECT MAX(id) AS max_id FROM mytable";
Cursor cursor = db.rawQuery(query, null);
int id = 0;
if (cursor.moveToFirst())
{
do
{
id = cursor.getInt(0);
} while(cursor.moveToNext());
}
return id;
This works for row ids that haven't been specified as "INTEGER PRIMARY KEY AUTOINCREMENT" (all tables have a row id column).
This is the best way to get the last ID on auto increment PRIMARY KEY with SQLITE
String query = "select seq from sqlite_sequence WHERE name = 'Table_Name'"
An important remark about the SQLITE_SEQUENCE table.
The documentation says
The SQLITE_SEQUENCE table is created and initialized automatically whenever a normal table that contains an AUTOINCREMENT column is created.
So the SQLITE_SEQUENCE table is created, but NOT the row associated with the table that contains the AUTOINCREMENT column. That row is created with the first insert query (with "seq" value of 1).
That means that you must doing at least one insert operation before looking for the next autoincrement value of a specific table. It could be done for example just after the creation of the table, performing an insert and a delete of a dummy row.
Here is what I use to get the next AUTOINCREMENT value for a specific table:
/**
* Query sqlite_sequence table and search for the AUTOINCREMENT value for <code>tableName</code>
* #param tableName The table name with which the AUTOINCREMENT value is associated.
*
* #return The next AUTOINCREMENT value for <code>tableName</code>
* If an INSERT call was not previously executed on <code>tableName</code>, the value 1 will
* be returned. Otherwise, the returned value will be the next AUTOINCREMENT.
*/
private long getNextAutoIncrement(String tableName) {
/*
* From the docs:
* SQLite keeps track of the largest ROWID using an internal table named "sqlite_sequence".
* The sqlite_sequence table is created and initialized automatically
* whenever a normal table that contains an AUTOINCREMENT column is created.
*/
String sqliteSequenceTableName = "sqlite_sequence";
/*
* Relevant columns to retrieve from <code>sqliteSequenceTableName</code>
*/
String[] columns = {"seq"};
String selection = "name=?";
String[] selectionArgs = { tableName };
Cursor cursor = mWritableDB.query(sqliteSequenceTableName,
columns, selection, selectionArgs, null, null, null);
long autoIncrement = 0;
if (cursor.moveToFirst()) {
int indexSeq = cursor.getColumnIndex(columns[0]);
autoIncrement = cursor.getLong(indexSeq);
}
cursor.close();
return autoIncrement + 1;
}
Inside the SQLiteOpenHelper you use, start a transaction. Insert some data and then rollback.
Such a way, you 'll be able to get the next row id, like this:
public long nextId() {
long rowId = -1;
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
try {
ContentValues values = new ContentValues();
// fill values ...
// insert a valid row into your table
rowId = db.insert(TABLE_NAME, null, values);
// NOTE: we don't call db.setTransactionSuccessful()
// so as to rollback and cancel the last changes
} finally {
db.endTransaction();
}
return rowId;
}
It's work.
public static long getNextId(SQLiteDatabase db, String tableName) {
Cursor c = null;
long seq = 0;
try {
String sql = "select seq from sqlite_sequence where name=?";
c = db.rawQuery(sql, new String[] {tableName});
if (c.moveToFirst()) {
seq = c.getLong(0);
}
} finally {
if (c != null) {
c.close();
}
}
return seq + 1;
}
You can use cursor.getInt(i); method
i here is index of the id column
Cursor c = db.rawQuery("Select * From mSignUp", null);
String mail = null;
try {
while (c.moveToNext()) {
mail = c.getString(0);
String pas = c.getString(1);
Toast.makeText(getApplicationContext(), "Name = " + mail + " Pass = " + pas, Toast.LENGTH_SHORT).show();
}
}catch (CursorIndexOutOfBoundsException e){
Log.e("OutOfBound", Log.getStackTraceString(e));
}
finally {
c.close();
}