Deleting multiple rows using IN sql statement in whereClause [duplicate] - android

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.

Related

SQLite - Querying a db where selectionArgs contains an array [duplicate]

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

Delete multiple rows in SQLITE (android) [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
I want to delete all the rows from the table that match the ids in an array. I can do that by either of the below 2 methods (both are working). Can you please advice which one is better?
METHOD 1:
public void deleteRec(String[] ids) { //ids is an array
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_NAME, KEY_ID+" IN (" + new String(new char[ids.length-1]).replace("\0", "?,") + "?)", ids);
db.close();
}
METHOD 2:
public void deleteRec(String[] ids) { //ids is an array
String allid = TextUtils.join(", ", ids);
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL(String.format("DELETE FROM "+TABLE_NAME+" WHERE "+KEY_ID+" IN (%s);", allid));
db.close();
}
Just forget about second method!
Your ids are all strings from numbers (otherwise SQL would fail), but for generic string data, passing data into SQL statements is never a good idea. Make you application vulnerable to SQL injection:
String.format("DELETE FROM t WHERE ID='%s', "1' AND 1=1 --")
// = "DELETE FROM t WHERE ID='1' AND 1=1 --'" => would delete all data!
and may fail your SQL statements:
String.format("DELETE FROM t WHERE v='%s', "It's me!")
// = "DELETE FROM t WHERE v='It's me!'" => syntactically incorrect (quote not escaped)!
EDIT: As ids are supplied as string array and probably KEY_ID refers a INT column, method 1 should be adapted to:
db.delete(TABLE_NAME, "CAST("+KEY_ID+" AS TEXT) IN (" + new String(new char[ids.length-1]).replace("\0", "?,") + "?)", ids);
Try this maybye it helps you :
String[] Ids = ......; //Array of Ids you wish to delete.
String whereClause = String.format(COL_NAME + " in (%s)", new Object[] { TextUtils.join(",", Collections.nCopies(Ids.length, "?")) });
db.delete(TABLE_NAME, whereClause, Ids);
read this documentation it says you should not use execSQL with INSERT, DELETE, UPDATE or SELECT because it can be a potential security risk. and still i believe regarding the performance the first one is better.
I used this:
String[] Ids = new String[]{"1", "2", "3"};
mContext.getContentResolver().delete(CONTENT_URI, ID + " IN (?, ?, ?)",Ids);
the multi ? work.
try this
public void deleteRec(String[] ids) { //ids is an array
SQLiteDatabase db = this.getWritableDatabase();
for (int i = 0; i < ids.length; i++) {
//Your code for delete
}
db.close();
}

How to update the table values

I have the table(shown in fig). I want to update the time column. I have the medicine_id(second column) how to update the different times depend upon the same medicine_id(second column).
Also, I have four rows in my DB when I updated time it should be two row means, I want to remove the other two rows with the particular medicine_id..
I have the following code for update:
if(mon) {
for (int i = 0; i < dosage_per_day; i++) {
mMedicineDbHelper.updateMedicines(new
MedicineTime(row_id, time_InMillies[i]));
}
}
My DB code:
public void updateMedicines(MedicineTime medicineTime) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(ID, medicineTime.getId());
values.put(MEDICINEID, medicineTime.getMedicine_id());
values.put(TIME, medicineTime.getTime_in_millis());
db.update(TABLENAME, values, MEDICINEID + " = ?",
new String[]{String.valueOf(medicineTime.getMedicine_id())});
db.close();
}
It shows exception..
anybody help to solve this...
For the original question asked you're executing the following code:
db.update(TABLENAME, values, MEDICINEID + " = ?",
new String[]{String.valueOf(medicineTime.getMedicine_id())});
For some reason on Android, converting numeric values to Strings and using them for selection args never works properly. See: Android Sqlite selection args[] with int values. The best way to do this is the following:
db.update(TABLENAME, values, MEDICINEID + " = " + medicineTime.getMedicine_id(), null);

How to delete rows in SQLite with multiple where args?

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);
👌

How do I use prepared statements in SQlite in Android?

How do I use prepared statements in SQlite in Android?
For prepared SQLite statements in Android there is SQLiteStatement. Prepared statements help you speed up performance (especially for statements that need to be executed multiple times) and also help avoid against injection attacks. See this article for a general discussion on prepared statements.
SQLiteStatement is meant to be used with SQL statements that do not return multiple values. (That means you wouldn't use them for most queries.) Below are some examples:
Create a table
String sql = "CREATE TABLE table_name (column_1 INTEGER PRIMARY KEY, column_2 TEXT)";
SQLiteStatement stmt = db.compileStatement(sql);
stmt.execute();
The execute() method does not return a value so it is appropriate to use with CREATE and DROP but not intended to be used with SELECT, INSERT, DELETE, and UPDATE because these return values. (But see this question.)
Insert values
String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();
Note that the executeInsert() method is used rather than execute(). Of course, you wouldn't want to always enter the same things in every row. For that you can use bindings.
String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
SQLiteStatement statement = db.compileStatement(sql);
int intValue = 57;
String stringValue = "hello";
statement.bindLong(1, intValue); // 1-based: matches first '?' in sql string
statement.bindString(2, stringValue); // matches second '?' in sql string
long rowId = statement.executeInsert();
Usually you use prepared statements when you want to quickly repeat something (like an INSERT) many times. The prepared statement makes it so that the SQL statement doesn't have to be parsed and compiled every time. You can speed things up even more by using transactions. This allows all the changes to be applied at once. Here is an example:
String stringValue = "hello";
try {
db.beginTransaction();
String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
SQLiteStatement statement = db.compileStatement(sql);
for (int i = 0; i < 1000; i++) {
statement.clearBindings();
statement.bindLong(1, i);
statement.bindString(2, stringValue + i);
statement.executeInsert();
}
db.setTransactionSuccessful(); // This commits the transaction if there were no exceptions
} catch (Exception e) {
Log.w("Exception:", e);
} finally {
db.endTransaction();
}
Check out these links for some more good info on transactions and speeding up database inserts.
Atomic Commit In SQLite (Great in depth explanation, go to Part 3)
Database transactions
Android SQLite bulk insert and update example
Android SQLite Transaction Example with INSERT Prepared Statement
Turbocharge your SQLite inserts on Android
https://stackoverflow.com/a/8163179/3681880
Update rows
This is a basic example. You can also apply the concepts from the section above.
String sql = "UPDATE table_name SET column_2=? WHERE column_1=?";
SQLiteStatement statement = db.compileStatement(sql);
int id = 7;
String stringValue = "hi there";
statement.bindString(1, stringValue);
statement.bindLong(2, id);
int numberOfRowsAffected = statement.executeUpdateDelete();
Delete rows
The executeUpdateDelete() method can also be used for DELETE statements and was introduced in API 11. See this Q&A.
Here is an example.
try {
db.beginTransaction();
String sql = "DELETE FROM " + table_name +
" WHERE " + column_1 + " = ?";
SQLiteStatement statement = db.compileStatement(sql);
for (Long id : words) {
statement.clearBindings();
statement.bindLong(1, id);
statement.executeUpdateDelete();
}
db.setTransactionSuccessful();
} catch (SQLException e) {
Log.w("Exception:", e);
} finally {
db.endTransaction();
}
Query
Normally when you run a query, you want to get a cursor back with lots of rows. That's not what SQLiteStatement is for, though. You don't run a query with it unless you only need a simple result, like the number of rows in the database, which you can do with simpleQueryForLong()
String sql = "SELECT COUNT(*) FROM table_name";
SQLiteStatement statement = db.compileStatement(sql);
long result = statement.simpleQueryForLong();
Usually you will run the query() method of SQLiteDatabase to get a cursor.
SQLiteDatabase db = dbHelper.getReadableDatabase();
String table = "table_name";
String[] columnsToReturn = { "column_1", "column_2" };
String selection = "column_1 =?";
String[] selectionArgs = { someValue }; // matched to "?" in selection
Cursor dbCursor = db.query(table, columnsToReturn, selection, selectionArgs, null, null, null);
See this answer for better details about queries.
I use prepared statements in Android all the time, it's quite simple:
SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();
If you want a cursor on return, then you might consider something like this:
SQLiteDatabase db = dbHelper.getWritableDatabase();
public Cursor fetchByCountryCode(String strCountryCode)
{
/**
* SELECT * FROM Country
* WHERE code = US
*/
return cursor = db.query(true,
"Country", /**< Table name. */
null, /**< All the fields that you want the
cursor to contain; null means all.*/
"code=?", /**< WHERE statement without the WHERE clause. */
new String[] { strCountryCode }, /**< Selection arguments. */
null, null, null, null);
}
/** Fill a cursor with the results. */
Cursor c = fetchByCountryCode("US");
/** Retrieve data from the fields. */
String strCountryCode = c.getString(cursor.getColumnIndex("code"));
/** Assuming that you have a field/column with the name "country_name" */
String strCountryName = c.getString(cursor.getColumnIndex("country_name"));
See this snippet Genscripts in case you want a more complete one. Note that this is a parameterized SQL query, so in essence, it's a prepared statement.
jasonhudgins example won't work. You can't execute a query with stmt.execute() and get a value (or a Cursor) back.
You can only precompile statements that either returns no rows at all (such as an insert, or create table statement) or a single row and column, (and use simpleQueryForLong() or simpleQueryForString()).
To get a cursor, you can't use a compiledStatement. However, if you want to use a full prepared SQL statement, I recommend an adaptation of jbaez's method... Using db.rawQuery() instead of db.query().

Categories

Resources