Hello I am pretty new with SQLite and I am trying to deal with some database manipulation in my project.
I have a table with almost 4000 rows and this is the format of every row:
problem_id (string)
problem_no (string)
problem_title (string)
dacu (int)
I need to query a bunch of problem_no based on the problem_id. The quantity of query is almost 1000 at a time. So I wrote a query code like this:
Set<Integer> getProblemsTitle(HashSet<String> problemsIDs) {
SQLiteDatabase db = this.getReadableDatabase();
HashSet<Integer> problemNo = new HashSet<Integer>();
Cursor cursor = null;
for (Iterator<String> iterator = problemsIDs.iterator(); iterator.hasNext();) {
cursor = db.query(CommonUtils.PROBLEM_TABLE, new String[] {
CommonUtils.KEY_PROBLEM_NO },
CommonUtils.KEY_PROBLEM_ID + "=?",
new String[] { iterator.next() }, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
problemNo.add(cursor.getInt(0));
}
cursor.close();
}
db.close();
Set<Integer> set = new TreeSet<Integer>(problemNo);
return set;
}
I know this is not a optimized snippet. And I need to optimize it a lot to reduce the execution time of the query. I did it inside AsyncTask but it is taking too much time.
How can I do this efficiently with faster performance?
You might want to consider taking this out of the database. If you just grabbed all the problems, you could add them all in code. Running one SELECT with 4000 results is still going to be much faster than a thousand SELECT statements.
The approach would be to grab them all, but sorted(ORDER BY problem_id). You could then just check each item in problemIDs against it, and add when you get a match.
You could also use the IN operator as Mathew suggests, but I don't know how efficient that will be with 1000 items in the set.
Don't iterate over a collection of IDs, but use the IN operator in a WHERE condition.
SELECT * FROM Table WHERE problem_id IN (1,2,3,4,5)
This will return all the records in the set. Whereas you are querying them one at a time.
You could try compiling a query, and maybe you can try to load the database into memory before reading.
Create an index on the problem_id column.
Related
I am new to android and maybe its a silly question but i am not getting it. See i am designing a game in which we give scores to some persons. So i want to store the names of the persons in a database while installation and then their scores set to 0 initially which will be updated according to what the users select. Here i am not able to figure out that how should i enter the data as it will be around 100 names and their scores. Using INSERT INTO() statement will make it like 100 statements. So is there any short method like can we do it through strings or something. Just guessing though. Any help would be appreciated.
You don't hard-code names or scores into your SQL statements. Instead, you use parameters.
var command = new SQLiteCommand()
command.CommandText = "INSERT INTO Scores (name, score) VALUES(#name, #score)";
command.CommandType = CommandType.Text;
foreach (var item in data)
{
command.Parameters.Add(new SQLiteParameter("#name", item.Name));
command.Parameters.Add(new SQLiteParameter("#score", item.Score));
command.ExecuteNonQuery();
}
and then just loop through all of the names and scores.
I recommend you using a transaction.
You can archive this stating you want to use a transaction with beginTransaction(), do all the inserts on makeAllInserts() with a loop and if everything works then call setTransactionSuccessful() to do it in a batch operation. If something goes wrong, on the finally section you will call endTransaction() without setting the success, this will execute a rollback.
db.beginTransaction();
try {
makeAllInserts();
db.setTransactionSuccessful();
}catch {
//Error in between database transaction
}finally {
db.endTransaction();
}
For the makeAllInserts function, something like this could work out:
public void makeAllInserts() {
for(int i = 0; i < myData.size(); i++) {
myDataBase = openDatabase();
ContentValues values = new ContentValues();
values.put("name", myData.get(i).getName());
values.put("score", myData.get(i).getScore());
myDataBase.insert("MYTABLE", nullColumnHack, values);
}
}
If you also want to know about the nullColumnHack here you have a good link -> https://stackoverflow.com/a/2663620/709671
Hope it helps.
Part of my functionality requires updating a value in every row (only happens rarely, when a user selects a certain setting).
Trouble is, the query takes a good few minutes to perform (at best), and there's only 269 test records. Is there any way this could be optimized?
String allRecords = "SELECT id, weight FROM Workout_Entry";
Cursor cursor = db.rawQuery(allRecords, null);
int rows = cursor.getCount();
int id;
double weight;
try
{
if (cursor.moveToFirst())
{
do
{
for (int i = 0; i < rows; i++)
{
id = cursor.getInt(0);
weight = cursor.getInt(1) / 2.2;
String strFilter = "id = " + id;
ContentValues args = new ContentValues();
args.put("weight", weight);
db.update("Workout_Entry", args, strFilter, null);
}
} while (cursor.moveToNext());
}
} finally
{
cursor.close();
}
(db).close();
Thanks!
Just push the work to the database engine instead of pulling the data out one row at a time and firing up a new update query each time. Replace your code with something like:
db.execSQL("UPDATE Workout_Entry SET weight=weight/2.2");
Also, since this seems to be some kind of metric/imperial unit conversion, consider keeping the data in just one format in the database and convert/format to the appropriate unit for display purposes.
You should learn to use transactions - you can see example of how you use that in this presentation of mine.
Also showing the impact of not using the transaction.
Ok, I've realised my error. I had a do while loop for every record, and inside of that was a for loop which looped through x many times, where x was the amount of existing records.
So it was going through O(n)² times instead of O(n).
Thanks to those who replied! I found both of your comments useful.
So I'm still building a Database to support a project of mine. There are two different things to be saved: first, attribute values of some player objects and second, simple values stored in a java class.
ATM my problem lies in the process of loading values of a player object and writing it in the respective class.
Now let's see some code:
Following you see the method I want to use for saving the values in the database.
That works fine atm, but I just realized I'm still passing the contentValues object an extra value for the 'ID' , which I did set - and planned to keep that way - as autoincrement.
Any Idea how to work this in accordingly?
public void savePlayer(Player player[]) {
for (int i = 0; i <= 3; i++) {
playerValues.put("ID", i);
playerValues.put("Name", player[i].getName());
playerValues.put("HP", player[i].getHp());
playerValues.put("Satisfaction", player[i].getsatisfaction());
playerValues.put("Hygiene", player[i].isHygieneInt());
playerValues.put("IsAlive", player[i].isAliveInt());
}
db.insert("playertable", null, playerValues);
}
Okay, hold on to your hats because this might look a bit like spaghetti - the load-method:
public void loadPlayer() {
String[] namecolumn = { "Name" };
String[] intcolumn = { "ID, HP, Satisfaction, Hygiene, IsAlive" };
String[] namesToString = new String[4];
for (int j = 0; j <= 3; j++) {
Cursor playerCursorName = db.query("playertable", namecolumn, "ID="
+ j, null, null, null, null);
namesToString = cursorToString(playerCursorName);
Resource.playerArray[j].setName(namesToString[j]);
}
for (int i = 0; i < 3; i++) {
int[] restToInt;
Cursor playerCursorInt = db.query("playertable", intcolumn, "ID="
+ i, null, null, null, null);
restToInt = cursorToInt(playerCursorInt, 4);
Resource.playerArray[i].setHp(restToInt[i]);
Resource.playerArray[i].setsatisfaction(restToInt[i]);
Resource.playerArray[i].setHygieneInt(restToInt[i]);
Resource.playerArray[i].setAliveInt(restToInt[i]);
}
}
Yeah, I know this looks pretty ugly but let me explain it:
Because there are 4 player objects I planned on iterating through the database entries by using the ID as identifier to get exactly one row at a time and writing the name and the other values of this object in the java class where I want to manage them within my project.
Note: same problem with autoincrement here than in the save method
In addition, I get a CursorIndexOutOfBoundsException when calling loadPlayer because
Index -1 is being requested - isn't that the result of an operation on the database resulting in an error?
Yeah that's pretty much it, I'll provide you with additional code if requested, hope someone can help me
You are using Cursors in a slightly odd way here.
The point of a Cursor is to ask SQLite to do the hard work of fetching data for you, and your job is simply to use the cursor to iterate through the returned values.
Firstly, I would change the query here to ask for all values in the table (and perhaps put some condition to constrain what you get back), to make sure your cursor then contains all your values.
Then, I would loop through the cursor's values by using a while loop, (with cursor.moveToPosition(-1) before the loop) moving along the cursor by using cursor.moveToNext().
See the API for more information:
http://developer.android.com/reference/android/database/Cursor.html
With regard to the autoincrement problem, as far as I can remember you can leave out the ID and use db.insert() without that field and the database will provide an ID for you.
You shouldn't have the same issue in your load method because it doesn't make sense to autoincrement when loading, you just get back what's in the database.
This is my first time using a database and I'm not really sure how this works. I made the database and made a query that returns a cursor and... now what? What is a cursor, really? Can I just use that to navigate through my data or do I have to put it in an ArrayList or ListActivity or what?
You need to iterate the cursor to get your results.
Use cursor.moveToFirst() and/or cursor.moveToNext() (with a while loop). Then you can use the getX() method, like cursor.getInt() or cursor.getString().
For example, ir your are expecting one result from your query:
if (cursor.moveToFirst()) {
String name = cursor.getString(cursor.getColumnIndex('NAME'));
int age = cursor.getInt(cursor.getColumnIndex('AGE'));
} else {
// oops nothing found!
}
First call cursor.moveToFirst(). Each time you call cursor.moveToNext() it will move to the next row. Make sure when you are done with your cursor you call cursor.deactivate() or you will get errors in your log cat.
Iterate over the returned Cursor instance
public List<Object[]> cursorToTableRows(Cursor cursor) {
List<Object[]> result = new ArrayList<Object[]>(cursor.getCount());
cursor.move(0);
cursor.moveToNext();
while (cursor.isAfterLast() == false) {
Object[] tableRow = new Object[cursor.getColumnCount()];
for(int i=0; i<cursor.getColumnNames().length; i++) {
int columnIndex = cursor.getColumnIndex(cursor.getColumnName(i));
String columnValue = cursor.getString(columnIndex);
tableRow[i] = columnValue;
}
result.add(tableRow);
cursor.moveToNext();
}
cursor.close();
return result;
}
Then create the desired objects.
public List<Vehicle> getVehicles() {
List<Vehicle> vehicles = new ArrayList<Vehicle>();
Cursor cursor = null;
List<Object[]> objects = cursorToTableRows(cursor);
for(Object[] row : objects) {
int i=0;
Vehicle vehicle = new Vehicle(row[i++].toString(), row[i++].toString()));
vehicles.add(vehicle)
}
return vehicles;
}
from Developer.android: This interface provides random read-write access to the result set returned by a database query.
In other words: query returns you a set of data represented by a cursor. First you need to make sure you got a valid cursor (not null) and then try to move it to desired position in the data set (use moveToXXX methods). In order to obtain data pointed by cursor use getXXX methods. When done using it make sure to call close to release resources.
According to this link it looks like you can iterate through the query return using something like:
cursor.next();
And grab the data at the location you are looking for using:
cursor.getString(0)
After you successfully have your Cursor setup, you would typically want to display that to a view in some form.
Have a look at the following answer for a detailed, but simple example of using a Cursor Adapter to pair up your newly-minted Cursor with your desired XML View:
https://stackoverflow.com/a/20532937/293280
I am having some trouble implementing a sqlite database in my simple android application:
a user is displayed a list of animals in a Listview.Upon selecting an animal the user is brought to an activity "Animal",which will display a picture of the animal and give them options to
view Animal Bio
Back
All very simple so far, right?
I have working the database, which will populate the listView of animals.Database currently looks like
Table Animal-
_ID,
Name
Table Biography-
_ID,
Bio
This is where I would welcome any helpful advice on my problem, or on how to improve my implementation.
Currently populating the DB as follows
long populateDB(){
String[] animalName = {"Lion" "Zebra", "Tiger", "Gorilla",...};
String[] animalBios = {"Found in the "...}
ContentValues animalNameVals = new ContentValues();
ContentValues animalBioVals = new ContentValues();
long[] rowIds = new long[animalName.length];
// Populate the animal table
for(int i = 0; i < animalName.length; i++){
animalNameVals.put(KEY_ANIMALNAME, animalName[i]);
rowIds[i] = db.insert(ANIMAL_TABLE, null, animalNameVals);
}
// Populate the Bio table
for(int j = 0; j < bios.length; j++){
animalBioVals.put(KEY_BIO, bios[j]);
rowIds[j] = db.insert(BIOS_TABLE, null, animalBioVals);
}
return rowIds[0];
}
And had planned on being able to tell database which animal on list was selected by passing extras with the intent, eg if position on listItemClick == 1, pass in tiger and retrieve tiger bio from db.
Problems:
Then on the Animal activity page is getExtra() == tiger, telling the activity that tiger was selected from the list and to load this bio from the DB..well, I cannot see an efficient method of implementation for this idea and am struggling to do so.
My second headache comes from adding the bio to the application from the Db.Originally I had a test bio hardcoded in a string, shown in a TextView.Is there a way to retrieve a string from a cursor and add it to the TextView id?I understand I will need some adapter, what I do not understand is why cant it be as simple as setResource(R.id.bio) = bio.
Thanks you for reading and any help is much appriciated.
First problem: First of all, I'm not sure why you don't have the column Bio in the Animal-table? As no Bio would fit to any other animal than itself, you can safely do this. By doing this you can query the database upon selection and pass the entire object (including name of animal and bio) to the next Activity and use this to get your information. If this was somewhat unclear, let me know and I'll try to explain it better.
Second problem: You can get values from tables (there of also Strings) using a Cursor. To get the String you can do something like this where cursor is the Cursor with your result from the database:
String bio;
// Move Cursor to its first element
if(cursor.moveToFirst()) {
// Make sure the cursor is not null
if(cursor != null) {
bio = cursor.getString(cursor.getColumnIndex("Bio")));
}
}
Sidenote: If I read the code correctly, it seems that you use long for ID's? The usual thing to go about ID's is integers as far as I know.