I have some code which executes two queries against a database and returns two cursor objects. Is there any way that I can combine these two cursors so that all the ListView gets the data from both?
There's MergeCursor for that (if there's no way to join tables).
FYI - An example of using MergeCursor()
c = Cursor containing Contacts columns from Contacts.CONTENT_URI
private Cursor mergeCursorSubset(Cursor c) {
int userMobile = ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
workMobile = ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE;
String storedNumber = ContactsContract.CommonDataKinds.Phone.NUMBER,
displayName =ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
numberType = ContactsContract.CommonDataKinds.Phone.TYPE,
contactKey = ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY,
whereClausePre = contactKey+" = '",
whereClausePost = "AND ("+numberType+" = '"+userMobile+"' OR "+numberType+" = '"+workMobile+"'";
Uri lookupUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;;
Cursor [] m = new Cursor[c.getCount()];
if (c.moveToFirst())
for (int k = 0; k<c.getCount();k++){
//Find the mobile numbers
Cursor u = this.getContentResolver().query(lookupUri,
new String[]{displayName, storedNumber, numberType}
, whereClausePre+c.getString(c.getColumnIndex(Contacts.LOOKUP_KEY))+"') "
+ whereClausePost, null, null);
int i = 0;
if (u.moveToFirst())
m[i++] = u;
} //for Each key
return new MergeCursor(m);
}
You can also use cwac-merge.
cwac-merge: Provides the MergeAdapter,
a ListAdapter that blends multiple
Views or ListAdapters into a single
ListAdapter. Use this for section
headings, blending multiple sources of
data together, etc.
Check out MatrixCursor.
Maybe this will help you also Android - Database Table Join
Related
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".
I don't know what's wrong with my code I follow the rule but I get wrong result. I want to search db and find all rows data but I only get last row from sqlite. my code to search database is bellow:
public ArrayList<ArrayList<ContractSaveDataFromDB>> ActiveContractData(String phone, String numberId)
{
ArrayList<ContractSaveDataFromDB> UserData = new ArrayList<ContractSaveDataFromDB>();
ArrayList<ArrayList<ContractSaveDataFromDB>> SendUserData =
new ArrayList<ArrayList<ContractSaveDataFromDB>>();
SQLiteDatabase db = this.getReadableDatabase();
String whereClause = "phone = ? AND numberId = ?";
String[] whereArgs = new String[]{
phone,
numberId
};
String orderBy = "activeContract";
Cursor res2=db.query("usersAccount",null,whereClause,whereArgs,null,null,orderBy);
res2.moveToFirst();
do{
UserData.clear();
int index;
ContractSaveDataFromDB contractSaveDataFromDB=new ContractSaveDataFromDB();
index = res2.getColumnIndex("buyAmount");
String buyAmount = res2.getString(index);
contractSaveDataFromDB.setBuyAmount(buyAmount);
UserData.add(contractSaveDataFromDB);
SendUserData.add(UserData);
} while(res2.moveToNext());
res2.close();
db.close();
return SendUserData;
I don't know what's wrong. I appreciate if you help me to solve my problem.
you already added where clause so maybe it is filtering your results try to remove it by change this
Cursor res2=db.query("usersAccount",null,whereClause,whereArgs,null,null,orderBy);
to this
Cursor res2=db.query("usersAccount",null,null,null,null,null,orderBy);
I believe that your issues is that you are trying to use an ArrayList of ArrayList of ContractSaveDataFromDB objects.
I believe that an ArrayList of ContractSaveDataFromDB objects would suffice.
It would also help you if you learnt to do a bit of basic debugging, as an issue could be that you are not extracting multiple rows.
The following is an alternative method that :-
uses the ArrayList of ContractSaveDataFromDB objects,
introduces some debugging by the way of writing some potentially useful information to the log
and is more sound, as it will not crash if no rows are extracted
i.e. if you use moveToFirst and don't check the result (false means the move could not be accomplished) then you would get an error because you are trying to read row -1 (before the first row) as no rows exists in the cursor.
:-
public ArrayList<ContractSaveDataFromDB> ActiveContractData(String phone, String numberId) {
ArrayList<ContractSaveDataFromDB> SendUserData = new ArrayList<ContractSaveDataFromDB>();
SQLiteDatabase db = this.getReadableDatabase();
String whereClause = "phone = ? AND numberId = ?";
String[] whereArgs = new String[]{
phone,
numberId
};
String orderBy = "activeContract";
Cursor res2 = db.query("usersAccount", null, whereClause, whereArgs, null, null, orderBy);
Log.d("RES2 COUNT", "Number of rows in Res2 Cursor is " + String.valueOf(res2.getCount()));
while (res2.moveToNext()) {
ContractSaveDataFromDB current_user_data = new ContractSaveDataFromDB();
current_user_data.setBuyAmount(res2.getString(res2.getColumnIndex("buyAmount")));
Log.d("NEWROW", "Adding data from row " + String.valueOf(res2.getPosition()));
SendUserData.add(current_user_data);
}
res2.close();
db.close();
Log.d("EXTRACTED", "The number of rows from which data was extracted was " + String.valueOf(SendUserData.size()));
return SendUserData;
}
If after running you check the log you should see :-
A line detailing how many rows were extracted from the table
A line for each row (if any were extracted) saying Adding data from row ? (where ? will be the row 0 being the first)
A line saying The number of rows from which data was extracted was ? (? will be the number of elements in the array to be returned)
This question already has an answer here:
Android:SQLite - Querying an Array in SelectionArgs as well as other string values
(1 answer)
Closed 8 years ago.
I have an array userNames which contains list of names and its not predetermined. I have to query a database which returns only the rowIDs which have any of the username from the array only if their availability is true. I think I can achieve this by using the code below but in my opinion its not efficient to use it.
ArrayList<Long> rowIDs = new ArrayList<Long>();
for (int i = 0 ; i < userNames.length ; i++) {
String selection = Columns.NAME + "=? and " + Columns.AVAILABILITY + "=?";
String[] selectionArgs = { userNames[i], true };
Cursor c = getContentResolver().query(Columns.URI,null,selection,selectionArgs,null);
if (c.getCount() > 0) {
rowIDs.add(c.getLong(c.getColumnIndex(Columns.ID)));
}
}
userNames in the array containing usernames. As it shows, db will be accessed again and again which isn't efficient especially if there are many user names. If possible, provide an efficient way to achieve it.
I donno where you require the thing.... I think we can use this String query="SELECT * FROM users WHERE username IN (?,?,.......)";
Cursor cur=db.rawQuery(query,new String[]{'name1','name2',....}); directly in the query. This is one method i remember.
But if you find something good do let me know too...
thx
Try this:
ArrayList rowIDs = new ArrayList();
String users="";
for (int i = 0 ; i < userNames.length ; i++) {
users=users+","+userNames[i];
}
String selection = Columns.NAME + " in (?) and " + Columns.AVAILABILITY + "=?";
String[] selectionArgs = { users[i], true };
Cursor c = etContentResolver().query(Columns.URI,null,selection,selectionArgs,null);
if (C.moveToFirst()) {
do{
rowIDs.add(c.getLong(c.getColumnIndex(Columns.ID)));
}while(c.moveToNext());
}
You can also use like instead of IN
My application stores three items: "name", "email", "mobile" in its database under the same table. I want to access each of them independently so that I could display their value under their respective TEXTVIEWs. I access them independently because if I access them all through one single function I won't be able to display the values
Currently, I have to write a function for each column so as to get information from it.
Example to get "email"
public String getUserEmail(String mobile) {
String[] columns = new String[] {USER_EMAIL};
Cursor c = MainDataBase.query(REG_INFO_TABLE, columns, USER_MOBILE_NUMBER + "=" + mobile, null, null, null, null);
String result = "";
int email = c.getColumnIndex(USER_EMAIL);
while(c.moveToNext()) {
result = result + c.getString(email);
}
return result;
}
So, if I need the the "name", I'll have to write another function similar to above to get it.
Now, if I need to access 50 such columns, I'll have to make 50 such functions which does not sound good. Is there any other way I can do this? Can arrays be used here?
You should consider creating a User class that holds the data for each User. Then you change getAllRegInfo() to return a User object. This has the advantage that the rest of your app only accesses Users and has no idea where the data comes from. It is also much more expressive to write
User user = getAllRegInfo();
String name = user.getName();
rather than
String[] user = getAllRegInfo();
String name = user[0];
In addition, a User class can have fields of any type rather than being forced to convert all data to String.
Okay, so I found an alternative. It is using arrays only.
Here is the code:
public String[] getAllRegInfo() {
String[] columns = new String[] {USER_NAME, USER_EMAIL, USER_MOBILE_NUMBER};
Cursor c = MainDataBase.query(REG_INFO_TABLE, columns, null, null, null, null, null);
String[] result = new String[3];
for(int j=0; j<3; j++) {
result[j] = "";
}
int[] column = new int[3];
column[0] = c.getColumnIndex(USER_NAME);
column[1] = c.getColumnIndex(USER_EMAIL);
column[2] = c.getColumnIndex(USER_MOBILE_NUMBER);
while(c.moveToNext()) {
for(int i = 0; i<3; ++i) {
result[i] = c.getString(column[i]);
}
}
return result;
}
Of course, one will have to manually asign the array indexes to the columns. Like this:
column[0] = c.getColumnIndex(USER_NAME);
column[1] = c.getColumnIndex(USER_EMAIL);
column[2] = c.getColumnIndex(USER_MOBILE_NUMBER);
The solution seems to be working fine but I'm not sure how scalable it is.
I have tried to get attendance name and emails using belwo code, but still not working.
Cursor cur = contentResolver.query(CalendarContract.Attendees.CONTENT_URI, new String[]{Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_RELATIONSHIP, Attendees.ATTENDEE_EMAIL}, Attendees.EVENT_ID +"= "+Long.parseLong("10"), null, null);
if(cur!=null){
SapGenConstants.showLog("cur size: "+cur.getCount());
while(cur.moveToNext()){
attendee_status = cur.getString(cur.getColumnIndex(Attendees.ATTENDEE_STATUS));
attendee_name = cur.getString(cur.getColumnIndex(Attendees.ATTENDEE_RELATIONSHIP));
attendee_Email = cur.getString(cur.getColumnIndex(Attendees.ATTENDEE_EMAIL));
SapGenConstants.showLog("attendee_status 2: "+attendee_status);
SapGenConstants.showLog("attendee_name 2: "+attendee_name);
SapGenConstants.showLog("attendee_Email 2: "+attendee_Email);
}
cur.close();
}
This is how you do it:
final String[] attendeeProjection = new String[]{
CalendarContract.Attendees._ID,
CalendarContract.Attendees.EVENT_ID,
CalendarContract.Attendees.ATTENDEE_NAME,
CalendarContract.Attendees.ATTENDEE_EMAIL,
CalendarContract.Attendees.ATTENDEE_TYPE,
CalendarContract.Attendees.ATTENDEE_RELATIONSHIP,
CalendarContract.Attendees.ATTENDEE_STATUS
};
final String query = "(" + CalendarContract.Attendees.EVENT_ID + " = ?)";
final String[] args = new String[]{"YOUR_EVENT_ID"};
final Cursor cursor = contentResolver.query(CalendarContract.Attendees.CONTENT_URI, attendeeProjection, query, queryArgs, sortOrder);
cursor.moveToFirst();
while (cursor.moveToNext()) {
// process the cursors
}
Since these queries are IO operations, it is advised to call them on a non-UI thread; my tip would be to go with RxJava.
If you don't want to work with cursors and queries (and a lot of boilerplate code), take a look at the CalendarWrapper library. It takes care of mapping objects and database rows to and from, CRUD operations can be performed with object methods. For example, this is how you'd query all attendees for an event ID:
final String query = "(" + CalendarContract.Attendees.EVENT_ID + " = ?)";
final String[] args = new String[]{"YOUR_EVENT_ID"};
final List<Attendee> attendees = Attendee.getAttendeesForQuery(query, args, null, getContentResolver());
i just ran into the same issue - and it was because of a mixup between the instance id and the event id, two distinct elements.
The important thing is that the attendees table is based on the EVENT id - and not the instance id - so it may explain your problem. Where do you get the hardcoded "10" from ?
If it's an instance id, then extract the EVENT id of the event series it belongs to, and pass that on to the ATTENDEES table. See if that helps.