I have a list of id's to search.
I want to call all data matching the values in this list.
Matching data is returned as a list.
I tried to do this in a loop and using IN but it didn't work.
When I write the values in parentheses one by one, it gives results, but how can I give the data in the list as a parameter?
db.query(_tableName, where: "id IN ('0001','00002','00003')")
When I type in the form, the query takes place. Can I send the incoming array directly into parentheses, not one by one? For example,
db.query(_tableName, where: "id IN (_ids)")
Query method:
Future<List<ContentModel>> getDiscoverContent(List<String> _ids) async {
Database db = await instance.database;
var contents = await db.query(_tableName, where: "id IN ('')");
List<ContentModel> _recommendedList = contents.isNotEmpty
? contents.map((e) => ContentModel.fromMap(e)).toList()
: [];
return _recommendedList;
}
The simplest solution I found:
var contents = await db.query(_tableName,
where:
"id IN('${_ids[0]}','${_ids[1]}','${_ids[2]}','${_ids[3]}','${_ids[4]}',"
"'${_ids[5]}','${_ids[6]}','${_ids[7]}','${_ids[8]}','${_ids[9]}')");
If you have a different solution, feel free to tell me.
You may try like this. This worked for my use case.
List<int> _ids = [1, 2, 3, 4];
// Loop through the _ids list and append quotes to each id
// add the element into a new List called new_Ids
List<String> new_Ids = [];
for (var i = 0; i < _ids.length; i++) {
new_Ids.add("'" + _ids[i].toString() + "'");
}
// use join to make a string from the new list where ids are separated by comma
String ids_Str = new_Ids.join(',');
Use db.query and pass ids string in where itself. Other columns in where condition can be passed in whereArgs if you need.
var resultList = await db.query('your_table_name',
columns: ['id', 'column2'],
where: 'id IN ($ids_Str)');
OR use rawQuery
var resultList = await db.rawQuery("SELECT id, column2 FROM your_table_name WHERE id IN(" + ids_Str + ")");
You may need to be careful of SQL Injection as we are passing dynamic values. Not sure about the best practices here.
Related
I would like to delete mutiple items from SQLite in batch basing on their ID column.
What I have is a HashMap which contains objects which one of field is pID (unique ID in DB).
So, here's my code:
/*
Delete rows from DB
*/
val selection = "${BaseColumns._ID} = ?"
// Create a list of product ID's to delete
val dbDeletor = dbHelper.writableDatabase
// Temp array to store ID's in String format
val tempIDs = ArrayList<String>()
// Loop through array of items to be deleted
for(i in ProductsRecyclerAdapter.productsToDeleteArray)
tempIDs.add(i.value.pID.toString())
// Perform deletion in DB
val deletedRowsCount = dbDeletor.delete(ProductsEntry.TABLE_NAME, selection, tempIDs.toTypedArray())
// Show snackbar with count of deleted items
Snackbar.make(mainCoordinatorLayout, "Products deleted: $deletedRowsCount", Snackbar.LENGTH_SHORT).show()
Everything works great when I'm deleting only 1 item but if tempIDs array contains 2 or more I'm receiving following Exception:
Too many bind arguments. 3 arguments were provided but the statement needs 1 arguments.
Maybe the reason is that I'm converting pID of type Long into String in order to delete rows in batch? I did not find any other solution. Please take a look and comment.
Your query looks somewhat like that:
DELETE FROM ProductsEntry.TABLE_NAME WHERE BaseColumns._ID = ?
There is only 1 argument ? but you're passing 3 values (IDs). You want to use IN statement instead, and print your params separated by comma:
// IN instead of equal to compare multiple values
val selection = "${BaseColumns._ID} IN (?)"
// your code to obtain IDs here
// .....
// combine all values into single string, ie. 1, 2, 3, 4 and wrap it as an array
val selectionArg = arrayOf(tempIDs.joinToString())
// Perform deletion in DB
val deletedRowsCount = dbDeletor.delete(ProductsEntry.TABLE_NAME, selection, selectionArg)
Is it possible to use an alias (AS) in a query for ORMLite in Android? I am trying to use it with the following code:
String query =
"SELECT *, (duration - elapsed) AS remaining FROM KitchenTimer ORDER BY remaining";
GenericRawResults<KitchenTimer> rawResults =
getHelper().getKitchenTimerDao().queryRaw(
query, getHelper().getKitchenTimerDao().getRawRowMapper());
But when this codes gets executed it gives the following error:
java.lang.IllegalArgumentException: Unknown column name 'remaining' in table kitchentimer
java.lang.IllegalArgumentException: Unknown column name 'remaining' in table kitchentimer
The raw-row-mapper associated with your KitchenTimerDao expects the results to correspond directly with the KitchenTimer entity columns. However, since you are adding your remaining column, it doesn't no where to put that result column, hence the exception. This is a raw-query so you will need to come up with your own results mapper -- you can't use the DAO's. See the docs on raw queries.
For instance, if you want to map the results into your own object Foo then you could do something like:
String query =
"SELECT *, (duration - elapsed) AS remaining FROM KitchenTimer ORDER BY remaining";
GenericRawResults<Foo> rawResults =
orderDao.queryRaw(query, new RawRowMapper<Foo>() {
public Foo mapRow(String[] columnNames, String[] resultColumns) {
// assuming 0th field is the * and 1st field is remaining
return new Foo(resultColumns[0], Integer.parseInt(resultColumns[1]));
}
});
// page through the results
for (Foo foo : rawResults) {
System.out.println("Name " + foo.name + " has " + foo.remaining + " remaining seconds");
}
rawResults.close();
I had the same problem. I wanted to get a list of objects but adding a new attribute with an alias.
To continue using the object mapper from OrmLite I used a RawRowMapper to receive columns and results. But instead of convert all columns manually I read the alias first and remove its reference in the column arrays. Then it is possible to use the OrmLite Dao mapper.
I write it in Kotlin code:
val rawResults = dao.queryRaw<Foo>(sql, RawRowMapper { columnNames, resultColumns ->
// convert array to list
val listNames = columnNames.toMutableList()
val listResults = resultColumns.toMutableList()
// get the index of the column not included in dao
val index = listNames.indexOf(ALIAS)
if (index == -1) {
// There is an error in the request because Alias was not received
return#RawRowMapper Foo()
}
// save the result
val aliasValue = listResults[index]
// remove the name and column
listNames.removeAt(index)
listResults.removeAt(index)
// map row
val foo = dao.rawRowMapper.mapRow(
listNames.toTypedArray(),
listResults.toTypedArray()
) as Foo
// add alias value. In my case I save it in the same object
// but another way is to create outside of mapping a list and
// add this value in the list if you don't want value and object together
foo.aliasValue = aliasValue
// return the generated object
return#RawRowMapper foo
})
It is not the shortest solution but for me it is very important to keep using the same mappers. It avoid errors when an attribute is added to a table and you don't remember to update the mapping.
I'm writing a Xamarin Android app which is using Parse.com as the backend. I'm running a query against a Parse Class called Beacons, of which one of the columns is a Pointer to another class called BeaconCat.
I'm therefore using two queries as shown below, but when it returns the data, it lists ALL of the categories within the BeaconCat class, not just the one which matches the initial query.
I'm expecting just one category, not all 13 of them. Any idea why?
// First query on class 1.
var innerQuery = ParseObject.GetQuery("Beacons");
innerQuery.WhereEqualTo("minor", minor);
// Query on class 2 which passes in first query.
var newQuery = ParseObject.GetQuery("BeaconCat");
newQuery.WhereMatchesQuery("Category", innerQuery);
IEnumerable<ParseObject> Myresults = await newQuery.FindAsync();
foreach (var result in Myresults)
{
var category = result.Get<string>("Category");
Console.WriteLine ("Category " + category);
}
I'm using the QueryBuilder to construct the inner SQL that later is used in a raw SQL to avoid escaping invalid characters manually.
SelectArg friendsIN = new SelectArg(friendsUsernames);
QueryBuilder<MyObject, Integer> qb = myObjectDao.queryBuilder();
qb.selectRaw("username", "MAX(time) AS latestTime").groupBy("username").where()
.in("username", friendsIN);
String innerSelect = pq.getStatement();
friendsUsernames is defined as ArrayList<String>.
Then I use the innerSelect to build the outer select:
String select = "SELECT w.id FROM (" + innerSelect +") AS x INNER JOIN myObject AS w on w.username = x.username AND w.time = x.latestTime";
GenericRawResults<String[]> results = myObjectDao.queryRaw(select);
But, as expected, the innerString has '?' and when I call queryRaw on myObjectDao I don't get any result. I tried to give friendsUsername as an array to queryRaw:
GenericRawResults<String[]> results =
myObjectrDao.queryRaw(select,
friendsUsernames.toArray(new String[friendsUsernames.size()]));
But I get the following error:
android.database.sqlite.SQLiteBindOrColumnIndexOutOfRangeException:
bind or column index out of range: handle 0x17a22e8
Any suggestions on how to accomplish this kind of queries with OrmLite?
Yeah that's not going to work. There is only one ? in your query and yet you are trying to pass in an array of user-names. There must be a 1-to-1 correspondence between the number of ? SQL arguments and the number of arguments passed to the queryRaw(...) method exactly.
If the friendsUsernames is a fixed size then you should be able to do something like the following which will generate SQL something like "in (?, ?, ?, ?)":
List<SelectArg> friendsInList = new ArrayList<SelectArg>();
for (int i = 0; i < NUM_FRIENDS; i++) {
// it doesn't matter what the value is since you just want the ?
fieldsInList.add(new SelectArg());
}
...in("name", friendsInList);
However if the list of names is dynamic then you are going to have to do this on the fly since, again, the number of ? must match the number of arguments passed to the queryRaw(...) method exactly.
I have one group table with a recursive relation, so each record has a parent_id. Given a group, I need to get all the student (each belong to a group) names in all its subgroups, but ordered by student name.
Do you know if there is any "easy" way to do it? If I have to do multiple queries, then I should order the results of the different Cursors, but Cursor has no orderBy().
Any ideas? Thank you so much!
As SQLite does not support recursive queries I implemented the select with two steps:
First, I have a method called getRecursiveDiningGroupIdsAsString() that retreives all the group ids recursively whose parent id is the one you pass by parameter. The result is a String in the form of: "(2, 3, 4)" so you can later use it in an IN clause. The method looks like:
public String getRecursiveDiningGroupIdsAsString(int depth, long diningGroupId) {
Cursor childDiningGroups = mDatabase.query(
"group",
new String[] {"_id"},
"parent_id = "+diningGroupId,
null, null, null, null
);
String recursiveDiningGroupIds = "";
while (childDiningGroups.moveToNext()) {
long childDiningGroupId = childDiningGroups.getLong(childDiningGroups.getColumnIndex("_id"));
recursiveDiningGroupIds += getRecursiveDiningGroupIdsAsString(depth+1, childDiningGroupId);
}
recursiveDiningGroupIds += diningGroupId;
if (depth > 0) {
recursiveDiningGroupIds += ", ";
} else {
recursiveDiningGroupIds = "("+recursiveDiningGroupIds+")";
}
return recursiveDiningGroupIds;
}
Once I have the group ids I need, I just do a simple query using the ids returned by the previous method and that is it!
Hope it helps!