I'm just starting to dive into some basic Android development and have been experimenting with a ListView and integrating it with a SimpleCursorAdapter. I look through a lot of online code samples, but I also have a book to use as a reference (Professional Android 2 Application Development).
In the book they work out an example To-Do list application that stores the list items in a SQLite database with an auto-incrementing, integer, primary key field.
A user can create new list items, but can also delete a selected item from the list. In the code, when the delete occurs, the primary key field is restricted (within the WHERE clause of the SQL statement) by the position attribute of the item as opposed to the item's rowid.
To me, this seems like an incorrect implementation. Looking at the SQLite documentation for AUTOINCREMENT, it says that this value will always increase and old values will never be re-used on the same table. So if you're deleting and adding things to the list, it would seem that the position and row id can get out of sync rather quickly.
Am I correct, then, to assume that the row id is the correct way to "index" into the database table and not the list position? I think the position would be safe to use if one is using the regular ListAdapter, but doesn't seem suitable when indexing into the database.
You can use the position to get a cursor to a particular list entry (and this cursor would be the 'row' in the 'table' corresponding to the row id):
Cursor cursor = (Cursor)parent.getItemAtPosition(pos);
int rowCol = c.getColumnIndex("_id");
Then you should see that cursor.getLong(rowCol) == id
That is definitely bad practice. I always use the row id to delete, and use the position id to retrieve the cursor's row id. I have the first edition of that book at home, I'm going to take a look at it myself later.
Related
Android, SQLite : I want to insert rows in between other rows in myTable using SQLite in android. For this, I am trying to increment ids of the all rows starting say row 3. So that I can insert a new row at position 3.
The primary key of myTable is column id. There are no other constraints in the table.
I have tried using the query mentioned in https://stackoverflow.com/a/9177264/6671004. This query does work in mySQL but not in Android (SQLite)
Here's the line of code :
database.execSQL("UPDATE myTable SET id = (id + 1) where id > 2 ORDER BY id desc");
Here's the error I'm getting on Android Studio (Compile time) :
https://imgur.com/a/9r0iyAa
This is the exception I'm getting if I remove 'ORDER BY id DESC' from the query :
java.lang.RuntimeException: Unable to start activity ComponentInfo{...}: android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: myTable.id (code 1555)
Is this the correct way to do this? Or is there a better way?
As pointed out by many, this is definitely not the correct way to go.
But I found workaround in case someone else is looking for a similar implementation.
Here it is :
UPDATE myTable SET id = - (id + 1) WHERE id > 1;
UPDATE myTable SET id = - id WHERE id < 0;
This is a hack which I found here.
Again, this is not the correct way to go. But just posting a working solution I found.
I have tried using the query mentioned in
https://stackoverflow.com/a/9177264/6671004. This query does work in
mySQL but not in Android (SQLite)
That question is tagged MYSQL. MYSQL has many differences from SQLite.
Here's the line of code :
database.execSQL("UPDATE myTable SET id = (id + 1) where id > 2 ORDER
BY id desc");
The SQLite UPDATE SQL takes the form of :-
i.e. there is no ORDER BY clause and hence the error saying that if you're going to use any KEYWORD then it must be a BETWEEN or IN or a ; (of course you could also extend the condition using AND OR and so on).
This is the exception I'm getting if I remove 'ORDER BY id DESC' from
the query :
The reason being is that the rowid (id being an alias of rowid) has an implied UNIQUE constraint and that the rows will be updated according to the id column in order. So if there are more than 3 rows (or have been and the last row has never been deleted) then when the id is 3, a row with 4 will exist and 3 + 1 = 4 so the row already exists and hence the UNIQUE constraint being encountered.
I want to insert rows in between other rows in myTable using SQLite in
android. For this, I am trying to increment ids of the all rows
starting say row 3. So that I can insert a new row at position 3.
In short that is not a good idea and is virtually definitely not needed.
Is this the correct way to do this? Or is there a better way?
Definitely no
At a guess you want a nice humanly understandable value so you can know what's going on. For example you may present a list with the sequence so you can then say delete the row that has a sequence of 3 and thus equate that to the id column. Fine until you present the list in a different order which may be more user friendly. Suddenly your sequence becomes complicated or even useless and if displayed confusing.
identifiers are intended to identify a row and allow fast access to that row as a numerical index will be more efficient (than a human easily readable non-numeric index) to process. They also cater for reducing unnecessary additional processing (shifting data).
An efficient methodology is presenting the list with tying the id to the position in the list (so position could be the nth element of an array that holds the respective id, regenerating the list (the underlying array) according to a different order so the nth element will still have the respective id).
Embarking on a process of shifting rows will impose additional resource usage for example extra disk IO whose cost is relatively expense. This will be compounded by the fact that you will have to process the rows in reverse order to get around the UNIQUE constraint, that in itself is going to require using even costlier methods because SQLite will by default try to perform the updates efficiently rather than cater for the efficiencies required to digress from recognised/accepted techniques that utilise the underlying efficiencies.
I found this one working. And remove autoincrement from id
String strSQL1 = "UPDATE people_table SET id = (id +1) WHERE id < 0";
String strSQL = "UPDATE people_table SET id = (id -1) WHERE id > 1";
db.execSQL(strSQL);
db.execSQL(strSQL1);
I have in my app a database with two tables : country and rights. Long story short, the db tells me whether a right (there is 10 rights in total) is legal or not in a specific country.
Now, I want the user to be able to search in my db by criterias. I have a layout with checkbox. If the user check a box, it mean he want to see every country in where the right is legal. For exemple, if he check the box "criteria1" and "criteria6", the user want the list of every country where criteria1 and criteria6 are legal, but we don't care wether the other rights are legal or not.
I asigned values to the checkboxs (1 if legal, 0 if illegal, just like in my db) and passes all of them to the activity who display the result of the search.
My problem is, I can't figure out how to search in my database. I need to only get the country where where the selected criters are equal to 1, but I don't know how to formulate my sql request (since I never know which criterias are going to be checked or not). My request need to only be about the criterias who has the value 1.
I had the idea of sending all my values to a function (witch returns a cursor) where I excecute a select statement if the value is equal to one, but I don't know how I could join all the result of my selects in a cursor. I also thought about using "CASE WHEN..." but it doesn't seem to work.
Does anyone have a clue on how I could deal with my search ?
If you need precisions on my problem, please ask.
This guy here (https://www.youtube.com/watch?v=NGRV2qY9ZiU&list=PL200JxfhYIgCrrpH4rCz-uNfBTb5sng1e) has the right idea.
The clip may be a bit slow but it does exactly what you want.
He creates a custom string based on if checkbox is checked and removes it from the string if unchecked.
To get what you want, you need to do a couple of things.
First, create a table with countries as rows, and rights as columns. Add 1 for right is present in country and 0 if not. Get this into an sqlite database (eg import via csv in DB browser for SQLite, free software; don't forget to create the android_metadata table in the sqlite database - search online for this). Import the database in the app (there is plenty of documentation for this online).
Second, change the text inputed in the if/else checkbox part of the script (he writes fruit names, you write for ex. "right1 = 1", or the exact query the checkbox should do on the column right1).
You also need to pay attention to the selection.add and selection.remove (know that selection is an array list which will store all your criteria for search by column).
Third, you need to change the content of his finalSelection (View view).
Delete all he has written and just create two strings:
String final1 = android.text.TextUtils.join(" or ", selection);
String final2 = "select country from table where " + final1;
The string final2 is your key for a cursor with a rawQuery. Just create a cursor in the database and pass the key to it. This can be done easily.
PS the method android.text.TextUtils.join() is amazing :)
You can place the operator of choice there.
If you need more than one operator (and, or etc), you can create different ArrayLists which you fill in the if/else checkbox is filled and join later in the finalSelection.
Oh, btw, if you have too many checkboxes, you will get a warning in the XML file (layout has more than 80 views is bad for performance).
In order to get around that, you need to get to know grid views a bit better. After reading a few tutorials on the basic use of GridViews, a good start for checkboxes inside them is here.
It may seem like a lot, but you need to learn to use holders to get information out of the getView of the modified BaseAdapter.
If you want to understand it better, follow the arrPath.
It is a String[] filled with all the paths of images found inside the cursor (string values from the dataColumnIndex, which contains paths of images).
Within the onClick() listener of the Button, from the arrPath he extracts only the rows of the cursor that were selected by checkbox click (thumbnailsselection[i] is a boolean - with a value TRUE/FALSE for each row in the cursor).
The selected paths are placed in the selectImages String, separated by OR.
Ok, I have a database with id column as timestamp
I made an activity list from the db.
I want to manage the db (delete rows) using the list, but the thing is I don't want to
View the whole timestamp, in every row I'll put only the time with some info and
I want to group the list ,as in contacts grouped by alphabet, by the date.
First, how can I make group in an activity list? (Making groups to the output list not the db)
Second, what is the best way to implement this? When user chooses an item and confims delete
I should delete it from the db but I have only patial timestamp...
(My only link to the db is the timestamp - I don't actually know where to store it in the list and I don't want to put it as a string in the text view, do a substring to get it back - is there another way to do this?)
I tried to search tthe web for some examples but I only found a simple ones.
Thnx :-)
?
I think what you're trying to do is create a database of tasks identified by a timestamp. You probably don't want to use a timestamp as a unique ID for the row. Instead, use an integer and qualify it as "PRIMARY KEY" when you create the database.
group the list? I'm not sure why you want to do this in the structure of the database. It's more common to group the list in the output, and leave the db itself in as flat a structure as possible.
Retrieve the primary key when you display a list of tasks. When the user clicks a task, use the primary key to choose the task to delete. You don't have to display the primary key; it serves as a behind-the-scenes "link" between the displayed info and the db row.
http://www.vogella.com/articles/AndroidListView/article.html
I should use cursor adapter for managing db.
And this one for grouping a list:
http://code.google.com/p/android-amazing-listview/
Thnx for the efforts
I completed the Notepad Tutorial Part 2 and everythings works fine.
But there is one thing I just can´t figure out why it is working ;)
The onListItemClick callback retreives the parameters:
ListView l - Check
View v - Check
position - Position of the item the user clicked on, starting with a zero based index (right?)
id - Row ID of the item the user clikced on
This seems to be the same rowId like used in the SqlLite DB table "notes"...but where the hell does
the ListActivity know about that we use this column as row Id?
I didn´t find any mapping between the List and the DB table, just the Cursor we bound in fillData.
But there is only a mapping between the Title column and the text1 id in the UI.
So where is the rowId binding? And what if I wanted to change that binding to another source?
Thx in advance
Alex
The SimpleCursorAdapter (in the fillData() method) binds the data of the database cursor to the listview. Whenever an item is clicked the CursorAdapter realizes which item is clicked and handes over the id to the listactivity.
This is NOT a rowID like in SQL. Databases are completely different from form elements.
rowID in this context is simply a 0-based index to tell you what you are clicking on. Your Cursor is simply filling data into the ListView from a table. Unless you are making direct queries with ContentProviders or SQLite queries, you shouldn't have to select the rows.
I hope this helps!
In my application I have a sqlite database that looks like this:
CREATE TABLE notes (_id integer primary key,
content text);
CREATE TABLE tags (_id integer primary key,
name text,
noteid integer,
foreign key(noteid) references notes(_id));
I'm storing text that can have some tags associated with it. Now I want to show this text and the tags in a ListView. However I can't figure out how to do this with a SimpleCursorAdapter. Is it even possible? My data might look like this:
sqlite> select * from notes;
1|foo bar baz
sqlite> select * from tags;
1|x|1
2|y|1
The query to get all notes and the data it returns looks like this:
sqlite> select notes._id, notes.content, tags.name from notes, tags where notes._id = tags.noteid;
1|foo bar baz|x
1|foo bar baz|y
Now, if I want to bind this data to the ListView in some way, how to do it? I would be happy if each row int the ListView contained two lines, one line with the content and one line with all the tags. Am I correct in guessing that the SimpleCursorAdapter won't help me here? What should I do instead?
SimpleCursorAdapter alone can't help you here.
If your goal is that you want one row to be one note + all its tags, you can try overriding bindView() in SimpleCursorAdapter and pouring in the tags that way. That would imply that you have already built up some sort of HashMap of note->tags and therefore can quickly determine the tags to go in the row.
To build up the HashMap, you have two choices that I see:
Build them on the fly by looking up the note in the HashMap, then doing a query to get the tags for that note if they're not found, caching them in the HashMap for later reuse (e.g., scrolling). The catch here is that you're doing a bunch of little queries (bad) and doing them on the main application thread while the user is scrolling (really bad).
Do one big query using an IN clause to get all tags for all notes, and convert the resulting Cursor into a fully-populated HashMap. Then, your per-row lookups will all succeed. This works well if you only have a modest number of rows; otherwise, this query may take longer than the user has patience for.
If your schema is flexible, you might consider whether you are better served with some amount of denormalization, such as having the tags in a single column of the notes table via a comma-delimited list or something. Even if that complicates write operations (e.g., putting tags in two places), if your reads greatly outnumber your writes, it may be worth it.