i'm new to programming with DB and not an expert in android programming so bear with me!
I have a DB with 2 tables (A and B) where I get a list of ID from the table A (1 to 100 rows) and get rows from table B for each id I got from table A giving me a total of 400 to 800 rows from table B.
This approach is not ideal for my app as it take 4 to 10+ seconds to process where I would ideally want less than 1s.
I'm trying to understand what would be best in a case like this.
Would having less row but more content in each help?
the DB is aprox 15mb Would loading it all in the background be better (I guess not as it would mean 5 min+ of loading)?
what is most expensive / have the worst performance: queries, cursor iteration, loading data from a field?
I have no specific index with my DB, would generating some help? If so how can I do that?
I currently have the bellow code collecting my data:
Long start = System.currentTimeMillis();
while (cursorTableA.moveToNext()) {
long id = cursorTableA.getLong(0);
int paragraphNunber = cursorTableA.getInt(1);
boolean isPoetry = (cursorTableA.getInt(2) != 0);
Paragraph mParagraph = new Paragraph(id, paragraphNunber,isPoetry);
// GET WORDS
String selectionWords = DbContract.WordsEntry.CONNECTED_PARAGRAPH_ID+ " = ?";
String[] selectionWordsArgs = new String[]{ Long.toString(paragraphNunber) };
String sortOrder = DbContract.WordsEntry.WORD_NUMBER+ " ASC";
Cursor cursorTableB = db.query(
DbContract.WordsEntry.TABLE_NAME,
DbContract.WordsEntry.DEFAULT_QUERY_COLUMNS_TO_RETURN,
selectionWords, selectionWordsArgs, null, null, sortOrder
);
while (cursorTableB.moveToNext()) {
String word = cursorTableB.getString(0);
String thesaurusRef = cursorTableB.getString(1);
String note = cursorTableB.getString(2);
mParagraph.addWord(new Word(word,thesaurusRef,note));
}
cursorTableB.close();
long finish = System.currentTimeMillis();
timeElapsed = finish- start;
System.out.println("DB Query => timeElapsed(s): "+(timeElapsed/1000)+" timeElapsed(ms): "+timeElapsed);
}
I should add that my DB is only used in read only, I copy it on first execution to the data/data/.../databases folder I never write in it.
I suggest you use a JOIN of your two tables and get just one cursor to iterate instead of get two and nest them. Try something like this:
final String MY_QUERY = "SELECT * FROM table_a a INNER JOIN table_b b ON a.id = b.other_id";
Cursor cursorTable = db.rawQuery(MY_QUERY, null);
while (cursorTable.moveToNext()) {
String id = cursorTable.getString(0);
String paragraphNunber = cursorTable.getString(1);
boolean isPoetry = (cursorTable.getInt(2) != 0);
String word = cursorTable.getString(3);
String thesaurusRef = cursorTable.getString(4);
String note = cursorTable.getString(5);
}
cursorTableB.close();
long finish = System.currentTimeMillis();
timeElapsed = finish- start;
System.out.println("DB Query => timeElapsed(s): "+(timeElapsed/1000)+"
timeElapsed(ms): "+timeElapsed);
Of course you will have to handle the logic of the creation of the object Paragraph as you will now have a cursor with as many rows as in tableB and "paragraph_id".
Related
I am trying to make very simple stuff, but I came with the conclusion that my code looks very ugly and I am pretty sure that there are some ways to improve the code performance and to clearness.
How can I do it only with an SQL statement in Android SQLITE?
I have two tables: A and B.
In A table columns d, p, u. And in table B columns d, p, u. The columns have the same names.
I need to find the last added row in each table where column p is some value. And update columns d and u from table B with values of columns d and u from table A.
final String selection = A.p + SQL_LIKE;
final String[] selectionArgs = new String[]{phone};
final String sortOrder = A.d + " DESC LIMIT 1";
final Cursor cursorA = getContentResolver().query(URI_A,
null, selection, selectionArgs, sortOrder);
if (cursorA != null && cursorA.moveToFirst()) {
final long dateTimeMillis = cursorA.getLong(cursorA.getColumnIndex(A.d));
final String selectionB = B.p + SQL_LIKE;
final String[] selectionArgsB = new String[]{'%' + phone};
final Cursor cursorB = getContentResolver().query(URI_A, null,
selectionB, selectionArgsB, A.d + " DESC LIMIT 1");
if(cursorB != null && cursorB.moveToFirst()){
final EntityB entityB = new EntityB().getUnitFromCursor(cursorB);
final ContentValues contentValues = new ContentValues();
contentValues.put(B.d, dateTimeMillis);
contentValues.put(B.u, durationMillis);
final String where = B._ID + SQL_ADD_VALUE;
final String[] whereArgs = new String[]{entityB.getId()};
getContentResolver.update(URI_B, values, where, whereArgs)
cursorB.close();
}
callLogCursor.close();
}
Question: How can I improve my code so in future if I need something the same I do not need to write so much boilerplate and it looks more clean. I am sure this code can be changed on a single SQL statement - I think this is the best solution for such problem.
UPDATE B
SET d = (SELECT d
FROM A
WHERE p LIKE ...
ORDER BY d DESC
LIMIT 1),
u = (SELECT u
FROM A
WHERE p LIKE ...
ORDER BY d DESC
LIMIT 1)
WHERE ID = (SELECT ID
FROM B
WHERE p LIKE ...
ORDER BY d DESC
LIMIT 1);
If it is possible that A does not have rows for a specific p in B, then you must add another filter to the outer WHERE clause (EXISTS (SELECT * FROM A WHERE ...)).
I have a database table with 2.5 million rows. I query this table by either one column (item_no) or two columns (item_no & subitem_no). In order to optimize my query I created two indexes. One for item_no and one for the pair of item_no and subitem_no.
Example:
CREATE INDEX Idx2 ON master(item_no);
CREATE INDEX Idx3 ON master(item_no, subitem_no);
Now when I run this query in SQLite browser:
SELECT component, hours, unit, price
FROM master
WHERE barcode = "234567"
AND item_no = "1234"
or this one:
SELECT component, hours, unit, price
FROM master
WHERE barcode = "234567"
AND item_no = "1234"
AND subitem_no = "34"
It executes extremely fast. Around 76ms - 186ms. This is what I want, the original select statement without the indexes took in between 4000 - 6000ms. So its the huge improvement I was looking for. So now I load the database onto my android device (Samsung Galaxy S6) and give it the same indexes. No improvement on query speed... at all, the select statement still takes 4000 - 6000ms to run.
Here's how I'm doing it. Please let me know if you see any errors or you can explain why I'm not seeing the expected performance increase.
db.execSQL("CREATE INDEX Idx2 ON master(item_no, subitem_no);");
db.execSQL("CREATE INDEX Idx3 ON master(item_no);");
public ArrayList<Data> getData(String barcode, String itemNo) {
ArrayList<Data> dataList = new ArrayList<>();
try {
Database db = Database.getInstance();
db.open();
String where = BARCODE_COLUMN + " = ? AND " + ITEM_NO_COLUMN + " = ?";
String[] columns = {COMPONENT_COLUMN, HOURS_COLUMN, UNIT_COLUMN, PRICE_COLUMN};
String[] args = {barcode, itemNo};
String sort = COMPONENT_COLUMN + " ASC";
Cursor cursor = db.getDb().query(true, MASTER_TABLE, columns, where, args, null, null, sort, null);
cursor.moveToFirst();
for (int i = 0; i < cursor.getCount(); i++) {
Data data = new Data();
data.setComponent(cursor.getString(cursor.getColumnIndex(COMPONENT_COLUMN)));
data.setHours(cursor.getString(cursor.getColumnIndex(HOURS_COLUMN)));
data.setUnit(cursor.getString(cursor.getColumnIndex(UNIT_COLUMN)));
data.setPrice(cursor.getString(cursor.getColumnIndex(PRICE_COLUMN)));
dataList.add(data);
if (!cursor.isLast()) {
cursor.moveToNext();
}
}
cursor.close();
db.close();
} catch (Exception e) {
e.printStackTrace();
}
return dataList;
}
It must of been a data cache issue. I restarted the device, uninstalled my app, then updated my index's per #Rotwang 's comments. Now when I run my app I get my desired 3ms query times.
Here were my fixes:
db.execSQL("CREATE INDEX Idx2 ON master(barcode, item_no, subitem_no);");
db.execSQL("CREATE INDEX Idx3 ON master(barcode, item_no);");
For my application, I need to query a sqlite database around 40-50 times. I am sure that the code I wrote is very inefficient. Unfortunately, I cannot find many examples online that involves querying the database many times.
String[] entryValArray = new String[indicesList.size()];
DBHelper dbHelper = new DBHelper(MainActivity.context);
SQLiteDatabase db = dbHelper.getReadableDatabase();
for (int i = 0; i < indicesList.size(); i++) {
int moddedIndex = Integer.parseInt(indicesList.get(i), 16) % DBHelper.numEntries;
String queryStr = "select * from " + DBHelper.TBL_NAME + " where " + DBHelper.IDStr +
" = " + Integer.toString(moddedIndex);
Cursor cursor = db.rawQuery(queryStr, null);
if (cursor.moveToFirst())
entryValArray[i] = cursor.getString(1);
cursor.close();
}
Basically, I am taking a list of strings, converting them to hex values, and then modding the value to get an index into a sqlite database. This is for a password generator application.
Is there a better way to do this, especially regarding creating a cursor and then closing it in every iteration.
First of all you have to change your query string as you need only one column value but you are using
Select *
instead of
Select yourColumn
. Secondly if your indices list size is not very large you can use
IN(values ) function of db instead of
" where " + DBHelper.IDStr +" = " + Integer.toString(moddedIndex);
this will return the result in only one query you don't have to run a whole loop.
I'm creating a simple financial app where the user can input an income or expense. I cannot find anywhere how I can change the "total" amount by adding or subtracting numbers inside the database. The easiest way I can explain it is:
user enters an income of $10 : So I would add that 10 into the database.
user enters an expense of -$5 : so i would also add that into the database
the end result should be $5 as the total, but how do I do this?
I'm completely stuck as I've never use SQLite before. Thanks
You can do that simply by firing 2 commands on SQL
a) Use Select to get the value from the SQLite Database
b) In Android programming add them or subtract them
c) Update the new Total into the database
public void updateExpense(decimal Expense,String Condition) {
double current = 0;
db = this.getReadableDatabase();
String selectQuery = "select id, total from " + TABLE_YourTable ;
Cursor cursor = db.rawQuery(selectQuery, null);
int RowID=0;
if (cursor.moveToFirst()) {
current= Double.parseDouble(cursor.getString(1));
RowID= Integer.parseInt(cursor.getString(0));
}
/// Now we use condition --> if condition is positive it mean add ... if condition is negative it means
////subtract
if(Condition.equals("positive"){
current += Expense;
}else {
current =current - Expense;
}
cursor.close();
db.close();
//Your Update to SQLite
db = this.getReadableDatabase();
ContentValues values = new ContentValues();
values.put(total , current );
db.update(TABLE_YourTable , values, KEY_ID + " = ?", new String[] { String.valueOf(RowID) });
db.close();
}
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/