I want to delete rows which satisfy any of multiple conditions.
For example, I pass a list of IDs, and I want to delete all rows with these IDs (IDs are unique).
This would be:
String[] ids = {"0", "1", "2", "3",...};
database.delete("rows" , "id=? OR id=? OR id=? OR id=? OR ..." , ids );
Is there any way to do it compact without multiple OR?
You may get it done through db.execSQL method and SQL's IN keyword. For example:
String args = TextUtils.join(", ", ids);
db.execSQL(String.format("DELETE FROM rows WHERE ids IN (%s);", args));
What you want to use is an IN clause, something like (with 4 IDs);
database.delete("rows", "id IN (?, ?, ?, ?)", ids );
Upon request, this is (one example of) how you can do it dynamically;
database.delete("rows",
"id IN (" + new String(new char[ids.length-1]).replace("\0", "?,") + "?)",
ids);
Android official documentation tells here that using execSQL is not the proper way to delete records.
I would like to suggest the following simple method. Using provided delete() api.
String[] idArray = new String[] {"1", "2", "3"};
String idsCSV = TextUtils.join(",", idArray);
SQLiteDatabase db = getWritableDatabase();
if (db != null) {
db.delete("table_name", "id IN (" + idsCSV + ")", null);
db.close();
}
You could use id IN (1, 2, 3, 4, ...) and directly print your array values inside the brackets:
database.delete("rows", String.format("id IN (%s)", StringUtils.join(ids, ",")));
As an alternative, I'd try to use some kind of flags column for such things (if there's something like being able to flag single entries for deletion; I don't know how your ID list is "built").
Here's an example which builds the "?, ?, ?, ..." placeholder string with StringBuilder. If there are many parameters, plain string concatenation would create lots of garbage, StringBuilder helps with that.
String[] ids = {"0", "1", "2", "3",...};
StringBuilder placeholders = new StringBuilder();
for (int i = 0; i < ids.length; i++) {
if (i != 0)
placeholders.append(", ");
placeholders.append("?");
}
String where = "id IN (" + placeholders.toString() + ")";
db.delete("rows", where, args);
I have done this using this :
Sqlite Statement Syntax :
db.delete(TABLE_NAME,"COLUMN IN (?)", new String[]{commaSaparatedString})
Example based on question :
String args = TextUtils.join(", ", ids);
db.delete("rows","id IN (?)", new String[]{args})
This work for my varchar ID.
public Integer deleteObjects(List<Object> objects){
if (objects.size()<1) return 0;
SQLiteDatabase db = getWritableDatabase();
StringBuilder strb= new StringBuilder();
for(int i = 0; i<objects.size();i++){
strb.append("'");
strb.append(objects.get(i).getId().toString());
strb.append("'");
// last element without comma
if (i<objects.size()-1) strb.append(",");
}
int rows = db.delete("table_name","id IN (" + strb + ")",null);
db.close();
return rows;
}
This build WHERE string with args directly. Equivalent to:
db.delete(TABLE_NAME,"id IN ('id0-maddog','id1-foo','id2-bar')",null);
👌
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.
I'm trying to execute a query on a SQLiteDatabase in Android, using the query() function. I want to pass the argument in SelectionArgs[], but when I'm using a IN statement, it doesn't seem to substitute the '?' with the correct argument.
My query looks like this:
temp = database.query(TABLE_NAME_ENTRIES,
new String[] {"_id", "Entry", "Summary"},
"_id IN ( ? )",
new String[] {ids}, null, null, null);
and it results in an empty Cursor. Debug gives me the information that the executed query uses a statement "_id IN ( ? )", showing that it doesn't seem to replace the '?' as expected. When I change the query to
temp = database.query(TABLE_NAME_ENTRIES,
new String[] {"_id", "Entry", "Summary"},
"_id IN ( " + ids + " )",
null, null, null, null);
instead, I get the expected result.
I'm really stupid on this problem, any ideas what I'm doing wrong?
You could use a workaround like this - creating a method that generates dynamically your string with ? and , and put it in the query like this:
String[] ids = { "id1", "id2" }; // do whatever is needed first
String query = "SELECT * FROM table"
+ " WHERE _id IN (" + makePlaceholders(ids.length) + ")";
Cursor cursor = mDb.rawQuery(query, ids);
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();
}
}
P.S. I think that the spaces before and after the question mark in your query could be wrong there, but I didn't test it, so I can't be 100% sure
In my app i want to update my database table based on two column.Means update salary where firstname="ekant" and last name="kancha".So can any body plz tell me what will be the query i have to write.
public int updateStatus(int salary,String fname,String lName)
{
ContentValues cv=new ContentValues();
String where = fname+ "=" + "ekanta";
cv.put("salary",salary);
return sdb.update(DATABASE_TABLENAME, cv, where, null);
}
this code works only when i want to update based on first name..But i want to update based on firstname and lastname.
plz help me.thanx
Use placeholders. This makes it easier to read the SQL query and protects against SQL Injection (accidental or otherwise).
public int updateSalary (int salary, String fname, String lName)
{
ContentValues cv = new ContentValues();
cv.put("salary", salary);
/* use COLUMN NAMES here */
String where = "firstname = ? and lastname = ?";
/* bind VALUES here */
String[] whereArgs = new { fname, lname };
return sdb.update(DATABASE_TABLENAME, cv, where, whereArgs);
}
If you have constants (e.g. private final static COLUMN_FNAME = "firstname") for the COLUMN NAMES, then you can build where using these constants.
However, do not put VALUES in the where string. Instead, use ? and supply any VALUES via the whereArgs array as per the above example.
Also, it is possible for people (even within the same organization) to share the same first name and last name. Basing the database queries/updates around such a pairing will break in such cases so it may be prudent to work on designing the API to work with a better record identifier.
use this...
String where = fname+ "=" + "ekanta" + " and " + lname + "=" + "your lastname";
I'm not sure what I'm doing wrong, but I'm trying to update a single integer value in a column of a table to 1 from 0. When creating the database, I set all values of the column to zero using:
for (int i = 0; i < setups.length; i++) {
ContentValues values = new ContentValues();
values.put(JokeDbContract.TblJoke.COLUMN_NAME_SETUP, setups[i]);
values.put(JokeDbContract.TblJoke.COLUMN_NAME_PUNCHLINE, punchlines[i]);
values.put(JokeDbContract.TblJoke.COLUMN_NAME_USED, 0);
db.insert(JokeDbContract.TblJoke.TABLE_NAME, null, values);
}
Then, in the actual activity, I'm doing:
private void findNewJoke() {
JokeDb jokeDb = JokeDb.getInstance(this);
SQLiteDatabase theDb = jokeDb.getDB();
String selection = JokeDbContract.TblJoke.COLUMN_NAME_USED + "=" + 0;
// Query database for a joke that has not been used, update the fields
// theJoke and thePunchline appropriately
String[] columns = {JokeDbContract.TblJoke._ID,
JokeDbContract.TblJoke.COLUMN_NAME_PUNCHLINE,
JokeDbContract.TblJoke.COLUMN_NAME_SETUP,
JokeDbContract.TblJoke.COLUMN_NAME_USED};
Cursor c = theDb.query(JokeDbContract.TblJoke.TABLE_NAME, columns, selection,
null, null, null, null);
if (c.moveToFirst() == false) {
Toast.makeText(this, R.string.error_retrieving_joke, Toast.LENGTH_LONG).show();
Log.e(getString(R.string.app_name),"No jokes retreived from DB in JokeActivity.findNewJoke()!");
}
else {
ContentValues values = new ContentValues();
theSetup = c.getString(c.getColumnIndexOrThrow(JokeDbContract.TblJoke.COLUMN_NAME_SETUP));
thePunchline = c.getString(c.getColumnIndexOrThrow(JokeDbContract.TblJoke.COLUMN_NAME_PUNCHLINE));
String updateSelection = JokeDbContract.TblJoke.COLUMN_NAME_SETUP + "=" + theSetup;
values.put(JokeDbContract.TblJoke.COLUMN_NAME_USED, 1);
theDb.update(JokeDbContract.TblJoke.TABLE_NAME, values, updateSelection, null);
}
}
I'm getting an error on the update:
java.lang.RuntimeException: .... while compiling: UPDATE jokes SET used=?
WHERE setup=Why do programmers always mix up Halloween and Christmas?
It seems as though I'm not getting an actual value set for the used column. What the program ultimately does is cycle through jokes where used=0, then sets used to 1 when it has been viewed. So the query only pulls those jokes that aren't used yet. I have a feeling I'm missing something simple, one can hope.
I think you are having problems with quotation marks.
Example:
String updateSelection = JokeDbContract.TblJoke.COLUMN_NAME_SETUP + "=\"" + theSetup + "\"";
However, the recommended way to do this, would be:
theDb.update(JokeDbContract.TblJoke.TABLE_NAME, values, JokeDbContract.TblJoke.COLUMN_NAME_SETUP + " = ?", new String[] { theSetup });
It is better to use field = ?, because this helps sqlite cache queries (I believe).
I have a contacts table and I need to get only certain IDs.
Imagine I have 10 contacts with ids from 1 to 10 and I want to get a Cursor with contacts 1 and 2.
A working code would be:
new CursorLoader(MyContentProvider.CONTACT_CONTENT_URI, null,
MyContentProvider.CONTACT_COLUMN_ID + " IN Â ( 1, 2 )",
null, null);
My problem is that I am unable to use the selectionArgs parameter since the escaping would break the query.
Can you think of a way of using the selectionArgs parameters and making this work?
If you want to use selectionArgs you can only use it like
String selection = "column IN (?, ?, ?)";
String[] selectionArgs = { "1", "2", "3" };
You need to build the "( ?, ?, ? )" string based on the number of arguments at runtime though. E.g.
private static String getSelection(int args) {
StringBuilder sb = new StringBuilder(MyContentProvider.CONTACT_COLUMN_ID + " IN (");
boolean first = true;
for (int i = 0; i < args; i ++) {
if (first) {
first = false;
} else {
sb.append(',');
}
sb.append('?');
}
sb.append(')');
return sb.toString();
}