I have a static sqlite database in Android. A function takes a int type of input and makes a query to the database. It's working fine upto input values of 97,500, but if I enter anything larger then one of two cases happens
If input is 98,000-99,500 it returns null
If input greater than 100,000 it returns the wrong data
Here's the function that's malfunctioning:
Budget getBudget(int income,String name)
{
Budget B=null;
Log.d("DB", String.valueOf(income));
try{
SQLiteDatabase db=this.getReadableDatabase();
Cursor cur=db.rawQuery("SELECT * FROM "+BudgetTable+" WHERE "+Low+"<=? AND "+High+">=?", new String[]{String.valueOf(income),String.valueOf(income)});
Log.d("DB", String.valueOf(cur.getCount()));
if(cur.getCount()!=0)
{
Log.d("DB", "Cursor not empty");
cur.moveToFirst();
B=new Budget(0, income,cur.getInt(cur.getColumnIndex(MortgageRent)),cur.getInt(cur.getColumnIndex(Utilities)) ,
cur.getInt(cur.getColumnIndex(LightnPower)), cur.getInt(cur.getColumnIndex(PhonenInternet)),
cur.getInt(cur.getColumnIndex(HomeMaintenance)), cur.getInt(cur.getColumnIndex(HomeCleaning)),
cur.getInt(cur.getColumnIndex(Groceries)), cur.getInt(cur.getColumnIndex(Clothing)),0,
cur.getInt(cur.getColumnIndex(PersonalGrooming)), cur.getInt(cur.getColumnIndex(MedicalnPharmacy)),
cur.getInt(cur.getColumnIndex(HealthInsurance)), cur.getInt(cur.getColumnIndex(LifeInsurance)),
cur.getInt(cur.getColumnIndex(HomeInsurance)), cur.getInt(cur.getColumnIndex(Accounting)),
cur.getInt(cur.getColumnIndex(BankFees)), cur.getInt(cur.getColumnIndex(Fuel)),
cur.getInt(cur.getColumnIndex(ServicenRepairs)), cur.getInt(cur.getColumnIndex(GovernmentCharges)),
cur.getInt(cur.getColumnIndex(CarInsurance)),0, cur.getInt(cur.getColumnIndex(PublicTransport)),
cur.getInt(cur.getColumnIndex(Entertainment)), cur.getInt(cur.getColumnIndex(SportsnGym)),
cur.getInt(cur.getColumnIndex(EatOut)), cur.getInt(cur.getColumnIndex(Alcohol)),
cur.getInt(cur.getColumnIndex(Gifts)), cur.getInt(cur.getColumnIndex(Holidays)),
cur.getInt(cur.getColumnIndex(NewspapernMagazine)), cur.getInt(cur.getColumnIndex(Others)), 0, 0, name);
Log.d("DB", String.valueOf(cur.getInt(cur.getColumnIndex(IncomeLevel))));
cur.close();
db.close();
}
}catch(Exception ex)
{
Log.d("DB", ex.getMessage());
}
return B;
}
Below is a screenshot of the data in database...I can't figure out why it doesn't work.
As I can't see your database, I am led to believe the source of your problem is this line:
Cursor cur=db.rawQuery("SELECT * FROM "+BudgetTable+" WHERE "+Low+"<=? AND "+High+">=?", new String[]{String.valueOf(income),String.valueOf(income)});
specifically the fact that you are using income for both greater-equal than AND less than-equal. Are you sure this is what you want? Are you sure it is not cuppose to be
Cursor cur=db.rawQuery("SELECT * FROM "+BudgetTable+" WHERE "+Low+"<=? AND "+High+">=?", new String[]{String.valueOf(lowIncome),String.valueOf(highIncome)});
You shouldn't pass numbers as strings, because strings have different rules of comparison, for example, the string "2000" will be considered greater than the string "102500".
They warn about it in the javadoc documentation:
selectionArgs You may include ?s in where clause in the query, which will be replaced by the values from selectionArgs. The values will be bound as Strings.
Your should rewrite your query like this:
Cursor cur=db.rawQuery("SELECT * FROM "+BudgetTable+
" WHERE "+Low+"<=" + income + " AND "+High+">=" + income);
Also, it is quite a common issue and some people encountered a similar problem: SQLite rawQuery selectionArgs and Integers Fields
Related
I have database with three columns.
I would like to query the database, based on one column which can have multiple values.
For single parameter we can the normal query method where cardName is String[]
Cursor cursor = database.query(Database.TABLE_COUPON_CARD, allColumns,Database.COLUMN_CARD_NAME + " = ?", cardName, null, null,null);
but if there are more than one value, I get a Android SQLite cannot bind argument exception
For multiple values of the same column we can use IN statement but, here how do I write the QUERY or how should i form the rawQuery
String whereClause = Database.COLUMN_CARD_NAME+ " IN(?)";
Cursor cursor = database.query(Database.TABLE_COUPON_CARD, allColumns,whereClause,new String[][]{cardName}, null, null,null);
Android QUERY doesnot take array of array.
What should the correct query be?
TEMPORARY SOLUTION
Currently I have created a method which dynamically creates the clause.
private static StringBuilder buildInClause(String[] myStringArray){
StringBuilder fullString=new StringBuilder();
fullString.append("(");
for(int i=0;i<myStringArray.length;i++){
fullString.append(" '"+myStringArray[i]+"' ");
if(i!=myStringArray.length-1){
fullString.append(",");
}
}
fullString.append(")");
return fullString;
}
If anyone has any other solution please do share.
For two values: IN(?,?). For three values: IN(?,?,?). Get the idea? Each ? corresponds to a single literal in the selection args array.
I've spent the whole day so far trying to get a select query to execute viarawquery or query, but I've had no luck so far.
The select statement I want to run is as the following:
SELECT * FROM h_word WHERE category='GRE' AND DONE=0 ORDER BY RANDOM() LIMIT 1
category is a TEXT type column and DONE is an INTEGER type with the default value of 0.
While the query works fine when executed directly in SQLite, in android,it doesn't return any results.
I've tried the below with no luck (the method is located in a class extended from SQLiteAssetHelper which itself is a helper class originally extended from SQLiteOpenHelper originaly taken from here: https://github.com/jgilfelt/android-sqlite-asset-helper:
public Cursor getRandomWord() {
Cursor c;
SQLiteDatabase db = getWritableDatabase();
c=db.rawQuery(query, null);
String query = "SELECT * FROM h_word WHERE category='GRE' AND DONE='0'
ORDER BY RANDOM() LIMIT 1 ";
c=db.rawQuery(query, new String[] {});
c.moveToFirst();
db.close();
return c;
}
I also tested with GRE instead of 'GRE' and 0 instead of '0' but it made no difference.
did the following as well:
public Cursor getRandomWord() {
Cursor c;
SQLiteDatabase db = getReadableDatabase();
c=db.query(true, "h_word", new String[] {
"_id",
"word",
"english_meaning"
},
"category" + "=?" + " AND " +
"DONE" + "=?",
new String[]{"GRE" ,"0"},
null, null, "RANDOM() LIMIT 1" , null);
c.moveToFirst();
db.close();
return c;
}
but the cursor remains empty.
Any ideas what I might be doing wrong here?
Any help would be much appreciated.
PS: when running a simple select statement without a where clause it, works fine.
After another few hours of struggling, I figured it's a bug in android's SQLiteDatabase class.
I managed to solve the problem by changing the name of the "category" column to something else.
Seems like "category" is a key word in the android SQLiteDatabase code, and makes a query return nothing when written in where clauses on the android side.
Someone else also had this problem here:
Android rawquery with dynamic Where clause
Im making an app on android and Im using an Sqlite database.
Basically, the app just inserts some numbers and then it navigates through all the rows to get the total sum of these numbers. It does that everytime the button is clicked.
But the problem is that, actually the app was working well. I just changed a little picture and then run the app again on my cell phone, but then it stop making the sum of these numbers. I used the debug option to see if the numbers were being inserted properly and the actually are. But I found out that the cursor is not getting any rows from the query. I dont know what the problem is. I really dont remmember altering the code, just changing the picture with paint. I checked the rows names, the tables names and everything is alright. I even did ctrl+z in a great desperation burst but nothing changed. I unistalled the app from my phone, clean, build and then run it again and same results.
here is the code that is involved in this operations.
public void crearTablas (SQLiteDatabase bd){
bd.execSQL("Create Table If Not Exists Gasto (ID Integer Primary Key, Descripcion Text Not Null, Costo Real Not Null, Fecha_Creado Datetime not null);");
}
public void insertarGasto(String descripcion, double costo, String fechaCreado{
ContentValues valores=new ContentValues();
valores.putNull("ID");
valores.put("Descripcion", descripcion);
valores.put("Costo", costo);
valores.put("Fecha_Creado", fechaCreado);
bd.insert("Gasto", null, valores);
}
public void crearGastoVar(String descripcion, double costo){
//redondear(double) is working well, i verified it!
bd.insertarGasto(descripcion, this.redondear(costo), fecha.get(Calendar.DATE)+"-"+(fecha.get(Calendar.MONTH)+1)+"-"+fecha.get(Calendar.YEAR));
}
public Cursor obtenerGastosVar(String fecha1, String fecha2){
return bd.query("Gasto", null, "Fecha_Creado between '"+fecha1+ "' and '"+fecha2+"'", null, null, null, null, null);
}
public double caluclarCostoGstsVar(){
Cursor c = bd.obtenerGastosVar("1-"+(fecha.get(Calendar.MONTH)+1)+"-"+fecha.get(Calendar.YEAR),
fecha.getActualMaximum(Calendar.DAY_OF_MONTH)+"-"+(fecha.get(Calendar.MONTH)+1)+"-"+fecha.get(Calendar.YEAR));
double costo=0;
if(c.moveToFirst()){ //This is returning false!!
do{
try{
Log.i("Look", "Sum is:"+costo);
costo+=Double.parseDouble(c.getString(c.getColumnIndex("Costo")));
}
catch(NumberFormatException e){
Log.i("Look", "Types are wrong");
return -2;
}
}while(c.moveToNext());
}
return redondear(costo);
}
String descrip_ = "number1";
double costo_ = 1000;
//I called before, crearTablas() so the tables are created before start inserting and reading
admin.crearGastoVar(descrip_, costo_);
admin.caluclarCostoGstsVar();
Try this format provided database is created properly
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
//your code
}
while (cursor.moveToNext());
}
cursor.close();
Hope it helps
Dates/times can be stored in several formats, but if you use strings and want to compare them, you must use a format like yyyy-mm-dd (with padded fields and the most significant field first) because otherwise, string comparisons would not give correct results.
Cursors can be read with a simple while loop:
Cursor c = ...;
while (c.moveToNext()) {
...
}
c.close();
(Functions like query never return a null cursor, and moveToNext does the right thing for a cursor that is positioned before the first entry.)
I have large number of strings, approximately 15,000 that I stored in a SQLite database using the following code:
void addKey(String key, String value, String table) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_KEY, key); // Contact Name
values.put(KEY_VALUE, value); // Contact Phone
// Inserting Row
db.insert(table, null, values);
db.close(); // Closing database connection
}
And then i search through that database using the following method in order to pick out any strings that match the key im looking for:
public String searchKeyString(String key, String table){
String rtn = "";
Log.d("searchKeyString",table);
// Select All Query
String selectQuery = "SELECT * FROM " + table;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Log.d("searchKeyString","searching");
if(cursor.getString(1).equals(key))
rtn = rtn + "," + cursor.getString(2);
} while (cursor.moveToNext());
}
cursor.close();
db.close();
Log.d("searchKeyString","finish search");
return rtn;
}
The goal is to do this in real time as the user is typing on the keep board so response time is key and the way it stands now it takes over a second to run through the search.
I considered reading all of the items into an array list initially and sorting through that which might be faster, but i thought an array list of that size might cause memory issues. What is the best way to search through these entries in my database?
A couple of things you can do...
Change the return to a StringBuilder until the end.
Only use a readable version of the database (that's probably not making much difference though)
Do not get a new instance of the database every time, keep it opened until you don't need it anymore
Query for only what you need with the "WHERE" argument in the SQL query.
See the code below with some changes:
// move this somewhere else in your Activity or such
SQLiteDatabase db = this.getReadableDatabase();
public String searchKeyString(String key, String table){
StringBuilder rtn = new StringBuilder();
Log.d("searchKeyString",table);
// Select All Query
String selectQuery = "SELECT * FROM " + table + " WHERE KEY_KEY=?";
Cursor cursor = db.rawQuery(selectQuery, new String[] {key});
// you can change it to
// db.rawQuery("SELECT * FROM "+table+" WHERE KEY_KEY LIKE ?", new String[] {key+"%"});
// if you want to get everything starting with that key value
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Log.d("searchKeyString","searching");
rtn.append(",").append(cursor.getString(2));
} while (cursor.moveToNext());
}
cursor.close();
Log.d("searchKeyString","finish search");
return rtn.toString();
}
Note even if you want this to happen in "real-time" for the user, you will still need to move this to a separate Thread or ASyncTask or you are going to run into problems....
You should consider using SELECT * FROM your-table LIMIT 50, for example. And you can put two buttons "Back", "Next" on your view. If every page has max 50 items, the user is at page 1, and he taps "Next", then you can use this query:
SELECT * FROM your-table LIMIT 50 OFFSET 50
If your table contains most of text-data, and you want to integrate search deeply into your app, consider using virtual table with FTS.
Let sqlite do the hard lifting.
First off, add an index to the field you're searching for, if you don't have one already. Secondly, don't do a SELECT all with manual table scan, but rather use a query in the form
SELECT column_value
FROM my_table
WHERE column_key LIKE "ABC%"
This returns the least amount of data, and the sql engine uses the index.
i dunno about better but maybe it'd be faster to make queries for the selected strings one by one.
public String searchKeyString(String key, String table){
String rtn = "";
Log.d("searchKeyString",table);
// Select All Query
String selectQuery = "SELECT * FROM " + table + "WHERE column_1 = " + key;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
rtn = rtn + "," + cursor.getString(2);
}
cursor.close();
db.close();
Log.d("searchKeyString","finish search");
return rtn;
}
EDIT:
Well i dunno how those custom keyboard apps do it, but those AutoCompleteTextViews are hooked up to adapters. you could just as easily make a cursorAdapter and hook your auto-complete view to it.
http://www.outofwhatbox.com/blog/2010/11/android-autocompletetextview-sqlite-and-dependent-fields/
http://www.opgenorth.net/blog/2011/09/06/using-autocompletetextview-and-simplecursoradapter-2/
I have this function that is filling out a class based on data from several tables. I got the first cursor:
String query="SELECT * FROM SESSION where _id =" + mSessionID + ";";
Cursor c = dbAdapter.selectRecordsFromDB(query, null);
Session session=null;
c.moveToFirst();
This works great. Then a little lower I do this:
long galleryId = c.getInt(4);
long packageId = c.getInt(5);
long contractId = c.getInt(6);
String query2="SELECT * FROM PHOTOPACKAGES WHERE _id =" + packageId + ";";
Cursor p = dbAdapter.selectRecordsFromDB(query2, null);
and the p cursor always returns -1 for its count. I can go right into the sqlite in the adb and run the same query where packageId = 1 and it works great...so not sure why this is not working, i don't see any other errors...can you just not use two cursors on the same database? p.s. selectRecordsFromDB is a helper function:
public Cursor selectRecordsFromDB(String query, String[] selectionArgs) {
Cursor c = myDataBase.rawQuery(query, selectionArgs);
return myDataBase.rawQuery(query, selectionArgs);
}
To answer your actual question: Yes you can target the same DB with multiple cursors. I believe there is something else wrong with your code.
Also as Philip pointed out, creating Cursors is very costly and you do not want to make extras just because, and always close them when finished with them.
Your selectRecordsFromDB function looks pretty darned weird, but it will probably work after a fashion, because the first cursor that you create goes out of focus straight away. Leaking open cursors like that is not a good idea though.