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
Related
This question already has answers here:
IN clause and placeholders
(9 answers)
Closed 5 years ago.
I have a list that performs multiple selections then delete the items that are selected using this method.
public Integer deleteDesc(ArrayList<String> rows) {
String[] args = rows.toArray(new String[rows.size()]);
Log.i("AMS", "Args: " + args);
db = this.getWritableDatabase();
return db.delete("DropdownList", "Description IN (?)", args);
}
where the parameter ArrayList contains the items that were selected. I works when I only select one item but returns an error "bind or column index out of range".
I'm pretty sure it's the whereClause which cause it because I'm not sure how to correctly use the "?"
I used this statement TextUtils.join(",", Collections.nCopies(args.length, "?"));
working code:
public Integer deleteDesc(ArrayList<String> rows) {
String[] args = rows.toArray(new String[rows.size()]);
db = this.getWritableDatabase();
return db.delete("DropdownList", "Description IN (" + TextUtils.join(",", Collections.nCopies(args.length, "?"))
+ ")", args);
}
You can build the WHERE IN clause using ? placeholders for each value using a prepared statement:
SQLiteDatabase db = this.getWritableDatabase();
StringBuilder sql = new StringBuilder("DELETE FROM yourTable WHERE Description IN (?");
for (int i=1; i < args.length; i++) {
sql.append(",?");
}
sql.append(")");
SQLiteStatement stmt = db.compileStatement(sql.toString());
for (int i=0; i < args.length; ++i) {
stmt.bindString(i+1, args[i]);
}
stmt.execute();
Note that using a prepared statement here is probably highly recommended, since you are deleting data. Allowing a SQL injection in this method could have bad side effects.
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 have researched a handful of other forums with a similar topic and I have yet to find an answer to this frustrating issue. I am trying to use an array to check if a column in my database has one of the multiple values in the array. My cursor is as follows:
public Cursor notificationQuery(String geoIds) {
Log.e("STRINGS", geoIds);
return mDb.query(Constants.TABLE_POI_NAME,
new String[]{Constants.TABLE_COLUMN_ID, Constants.TABLE_COLUMN_POI_NAME,
Constants.TABLE_COLUMN_LATITUDE, Constants.TABLE_COLUMN_LONGITUDE,
Constants.TABLE_COLUMN_GEO_ID},
Constants.TABLE_COLUMN_GEO_ID + " IN (?)",
new String[]{geoIds},
null, null, null, null);
}
geoIds is currently an array of two values which has been converted into a string. The logged value of that string is below:
21007b0f-6b20-4eff-9a76-b412db8daa2e,26c695d6-6cb4-4c74-9933-281813a06fd9
Those are to separate Id values separated by a comma. When I test each one individually using "= ?" instead of "IN (?)" I get a proper match with the database and my cursor returns a value. However, when combined my cursor returns nothing when it should return two rows from the database. Please help me solve this issue! Thanks!
Consider a function String makePlaceholders(int len) which returns len question-marks separated with commas, then:
public Cursor notificationQuery(String geoId1,String geoId2) {
//assume we split this geoIds to 2 different values. you need to have 2 strings no 1
String[] ids = { geoId1, geoId2 }; // do whatever is needed first depends on your inputs
String query = "SELECT * FROM "+ Constants.TABLE_POI_NAME + " WHERE "+
Constants.TABLE_COLUMN_GEO_ID +" IN (" + makePlaceholders(names.length) + ")";
return mDb.rawQuery(query, ids); // ids is the table above
}
Here is one implementation of makePlaceholders(int len):
String makePlaceholders(int len) {
if (len < 1) {
// It will lead to an invalid query anyway ..
throw new RuntimeException("No placeholders");
} else {
StringBuilder sb = new StringBuilder(len * 2 - 1);
sb.append("?");
for (int i = 1; i < len; i++) {
sb.append(",?");
}
return sb.toString();
}
}
Or more simply, use the geoIds variable directly:
public Cursor notificationQuery(String geoIds) {
Log.e("STRINGS", geoIds);
return mDb.query(Constants.TABLE_POI_NAME,
new String[]{Constants.TABLE_COLUMN_ID, Constants.TABLE_COLUMN_POI_NAME,
Constants.TABLE_COLUMN_LATITUDE, Constants.TABLE_COLUMN_LONGITUDE,
Constants.TABLE_COLUMN_GEO_ID},
Constants.TABLE_COLUMN_GEO_ID + " IN (" + geoIds + ")",
null, null, null, null, null);
}
This approach is less secure but will likely give you the result you expect.
I'm trying to make a query where I get data from two tables but it won't show me any result. I know there is a result because in SQLite3 it displays at least one, e.g.
sqlite> select eventthemename, eventtypename from event_type, event_theme
...> where event_type.eventthemes = event_theme._id
...> and event_type._id = '2';
Tribal | Dance
I'm using a content provider. Does anyone have an idea on how to make this?
Your question isn't really clear (you should include some of your code!), so I might not answer your question as you hoped. But following is a simple example I have where I join two tables in one query:
private static final String QUERY_ALL_COURSES_WITH_SEMESTER =
"SELECT * FROM Course JOIN Semester ON semesterId = courseSemesterId";
public Course getCourseByCodeAndSemester(String courseCode, Semester semester)
{
Course result = null;
String whereClause = " WHERE courseCode = '" + courseCode.toUpperCase() + "'"
+ " AND semesterId = " + semester.getInternalId();
Cursor cursor = db.rawQuery(QUERY_ALL_COURSES_WITH_SEMESTER + whereClause, null);
if (cursor.moveToFirst())
{
result = Course.createFromCursor(cursor);
}
cursor.close();
return result;
}
Note that there are probably many things that could be optimized in this function, but it illustrates how it can be done.
As I'm using content providers I ended doing something not quite fast but that works:
themes = managedQuery(
EventTheme.CONTENT_URI,
PROJECTIONTHEMES,
EventTheme.EVENTTYPE + "= ?",
new String[] {eventType},
EventTheme.DEFAULT_SORT_ORDER);
String[] from = new String[] {
Event._ID,
Event.NAME,
Event.STARTDATE,
Event.ENDDATE,
PointOfInterest.POINTOFINTERESTNAME
};
int[] to = new int[] {
R.id.event_id,
R.id.name,
R.id.start_date,
R.id.end_date,
R.id.location_name
};
while (themes.getPosition() < themes.getCount() - 1) {
themes.moveToNext();
eventTheme = themes.getString(themes.getColumnIndexOrThrow(EventTheme._ID));
events = managedQuery(
Event.CONTENT_URI,
PROJECTIONEVENTS,
Event.EVENTTHEME + "= ?",
new String [] { eventTheme } ,
Event.DEFAULT_SORT_ORDER);
}
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