New to Android - Is _id a must for databases? - android

I have an already functioning app running on iOS whose database uses a composite primary key. For discussions sake, lets say "CID" and "RID" make up that composite pk, resulting in something that looks like:
CID-RID
F6uuDTEU1c-1
F6uuDTEU1c-2
F6uuDTEU1c-3
However, there are conditions under which the CID column is altered, resetting the RID column. For example:
CID-RID
...
F6uuDTEU1c-4
F6uuDTEU1c-5
WQq6JnyrDI-1
WQq6JnyrDI-2
WQq6JnyrDI-3
...etc
These databases are to be shared cross-platform (ios - android) and going back and editing the current ios structure is not an option. What issues am I going to run into not having an _id column as my pk running on Android?
I found this here on SO - which seems to state that the db itself does not have to have the _id column, only that ...
"The result set for the cursor must contain _id, not the cursor itself."
... but I could be reading this all wrong. Any input/help is much appreciated.
PS: I already looked at a few (what I thought were) similar questions here, here, and here.

You are free to have any database schema you want. Android doesn't impose any additional restrictrions there.
Only if you use a CursorAdapter, then the Cursor needs an _id column. Any app can be written without using CursorAdapter, it's just there to provide some convenience. sqlite tables always have a ROWID column that aliases to the INTEGER PRIMARY KEY column if the table has one. You can always select it as the _id, e.g. SELECT rowid AS _id ... if needed.

Related

SQLite CREATE table with an alias for ROWID

Looking at this answer I was trying to work out how to actually create a table with a column that is just an alias for ROWID. I would like to do this as some android classes require a unique column named '_id' but i dont want that col to be a PRIMARY KEY. I cant use AUTO_INCREMENT on the col that not a PK either, so I would just like _id to be an alias. I know I could do that in the query but thats not practical for my situation. Any pointers? Is this even possible?
Thanks :)
EDIT: really i just want to be able to have my own PK but also have an id field for CursorAdapters to work correctly etc
EDIT: Looking at Do i have to use _ID as a SQlite primary key? and does it have to be an INT? (Android Dev) it suggests to do it in the query if my PK is a number type also, but what if its a TEXT col that im using for my PK? (sort of thinking aloud here) - I guess i could copy CursorAdaptor and just getString instead of a long for the _id col (plus, pass the name of the col to use as the PK in to cursor adapter, get rid of _id!) OR just add a alias for the ROWID as _id in the SELECT, feels a bit hacky though...
You may have a primary key that is completely independent of SQLite's built-in rowid.
Every row of every SQLite table has a 64-bit signed integer key that uniquely identifies the row within its table. This integer is usually called the "rowid". The rowid value can be accessed using one of the special case-independent names "rowid", "oid", or "rowid" in place of a column name.
You may formulate your query so that the built-in rowid is called _id for the benefit of the CursorAdaptor by prepending the hidden rowid column to your results:
select _rowid_ as _id, ...
The rowid is unique, but need not be the primary key. You can make the primary key anything you like in the CREATE TABLE statement.
TBH, I don't think that this is really feasible. You are required to have that kind of PK column (even though with a lot of hacking you could create some kind of workaround).
I would rather suggest to have the _id column as a technical PK and have an additional column as logical PK i.e. just a freely defined column with a unique constraint. Your program logic should then be able to simply use the logical PK for all operations. Of course this would require you to use custom queries for find operations etc. but that is usually more fitting anyways.
hth..

SQLite rename fts3 rowid column

I'm using a tutorial here to provide an AutoCompleteTextView with a SimpleCursorAdapter. It works perfectly as is, but I've changed the database to use fts3 because I've heard it's faster (hence, the name).
It seems something in the code is hard-wired to use the column _id because after changing to an fts3 table, I get this error:
01-28 21:31:53.018: E/AndroidRuntime(16284): java.lang.IllegalArgumentException: column '_id' does not exist
01-28 21:31:53.018: E/AndroidRuntime(16284): at android.database.AbstractCursor.getColumnIndexOrThrow(AbstractCursor.java:314)
Even though I haven't declared the autoincrement key anywhere (as it's superceded by rowid in fts3). The error occurs in AbstractCursor, so there's not much I can do about it.
I'm thinking there might be a way to force the code to recognize rowid as _id by using SELECT rowid,* FROM mytable and then changing the column name somehow.. I'm pretty new to sql so any help is appreciated!
Renaming a column in SQLite can be done as described here. Note that it is highly advisable to do all these operations in transaction. One detail when you are doing this in Android - I don't know how you execute sql scripts in your solution, but keep in mind this if you use execSQL calls.
By the way if you prefer not to rename the column you can try the technique proposed here.

Can you delete columns in an SQLite database?

The Android app that I am currently working on dynamically adds columns to an SQLite database. The problem I have is that I cannot figure out a way to remove these columns from the database.
If I add column A, B, C, D, and E to the database, is it possible to later remove column C?
I have done a lot of looking around and the closest thing I could find was a solution that requires building a backup table and moving all the columns (except the one to be deleted) into that backup table.
I can't figure out how I would do this, though. I add all the columns dynamically so their names are not defined as variables in my Java code. There doesn't seem to be a way to retrieve a column name by using Android's SQLiteDatabase.
SQLite has limited ALTER TABLE support that you can use to add a column to the end of a table or to change the name of a table.
If you want to make more complex changes in the structure of a table, you will have to recreate the table. You can save existing data to a temporary table, drop the old table, create the new table, then copy the data back in from the temporary table.
For example, suppose you have a table named "t1" with columns names "a", "b", and "c" and that you want to delete column "c" from this table. The following steps illustrate how this could be done:
BEGIN TRANSACTION;
CREATE TEMPORARY TABLE t1_backup(a,b);
INSERT INTO t1_backup SELECT a,b FROM t1;
DROP TABLE t1;
CREATE TABLE t1(a,b);
INSERT INTO t1 SELECT a,b FROM t1_backup;
DROP TABLE t1_backup;
COMMIT;
SQLite doesn't support a way to drop a column in its SQL syntax, so its unlikely to show up in a wrapper API. SQLite doesn't often support all features that traditional databases support.
The solutions you've identified make sense and are ways to do it. Ugly, but valid ways to do it.
You can also 'deprecate' the columns and not use them by convention in newer versions of your app. That way older versions of your app that depend on column C won't break.
Oh... just noticed this comment:
The app is (basically) an attendance tracking spreadsheet. You can add
a new "event" and then indicate the people that attended or didn't.
The columns are the "events".
Based on that comment you should just create another table for your events and link to it from your other table(s). You should never have to add columns to support new domain objects like that. Each logical domain object should be represented by its own table. E.g. user, location, event...
Was writing this initially. Will keep it if you're interested:
Instead of dynamically adding and removing columns you should consider using an EAV data model for that part of your database that needs to be dynamic.
EAV data models store values as name/value pairs and the db structure never needs to change.
Based on your comment below about adding a column for each event, I'd strongly suggest creating a second table in which each row will represent an event, and then tracking attendance by storing the user row id and the id of the event row in the attendance table. Continually piling columns onto the attendance table is a definite anti-pattern.
With regards to how to find out about the table schema, you can query the sqlite_master table as described in this other SO question - Is there an SQLite equivalent to MySQL's DESCRIBE [table]?
As per SQLite FAQ, there is only limited support to the ALTER TABLE SQL command. So, the only way you can do is that ou can save existing data to a temporary table, drop the old table, create the new table, then copy the data back in from the temporary table.
Also you can get the column name from the database using a query. Any query say "SELECT * FROM " gives you a cursor object. You can use the method
String getColumnName(int columnIndex);
or
String[] getColumnNames();
to retrieve the names of the columns.

SQLite-how to compare two column

friends,
I am doing an Android project in my company, still some small work is remaining, I need your help to complete the task.
The problem is...
I have created two tables in which, table1 has an empty column, for purpose for saving name...
The table2 has a list of names, the objective is only the names from this list should be should be saved in the table1's empty column other than that it shouldn't accept any of the name typed manually.
You appear to want to make the list of names a validation: if the user wishes to save a name to table1, the name must already exist in table2.
Typically this would be done as in the following example, in which only the products listed in PRIZEPRODUCTS can be entered into PRIZEWINNERS table: someone could not win a Boat, for example, given the data below:
PRIZEPRODUCTS
id
productname
1|TV
2|iPad
3|backpack
PRIZEWINNERS
id
productid
winner
ALTER TABLE PRIZEWINNERS
ADD CONSTRAINT PRIZEWINNERS_PRIZEPRODUCTS_FK
FOREIGN KEY(productid) REFERENCES PRIZEPRODUCTS(id)
SQLite doesn't create the foreign key using ALTER TABLE but as part of the create-table statement. See here for the syntax. For enabling foreign key support in Android (2.2), see here.
Now, you can establish the foreign key on the [productname] column if [productname] were the key of PRIZEPRODUCTS. In other words, you could make person-name the key of the table rather than having a PersonID. But if that name is changed in the validation table, it can break the foreign key relationship, unless ON UPDATE CASCADE is enabled, but I am not sure if this is supported in Android.
I hope below query will work for you.
insert into table1(name) values (select name from table2 where id=?).
Thanks.

Android's SimpleCursorAdapter with queries using DISTINCT

Here's an interesting question that I'm shocked hasn't been asked more often on the internet. Android's CursorAdapters are extremely useful once you get a ContentProvider up and running and learn how to use it, but they are limited due to their requirement on having the _id field as part of their query (an error is thrown without it). Here's why:
My specific problem is that I have two spinners: One spinner should contain unique "category" names from the database, and the other should populate with all the database entries from the selected "category" (category being the column name, here). This seems like a pretty simple setup that many programs might use, no? Trying to implement that first spinner is where I've run into problems.
Here's the query that I would like for that first spinner:
SELECT DISTINCT category FROM table;
Making this query throws an error on CursorAdapter because the _id column is required as part of the query. Adding the _id column to the projection naturally returns every row of the table, since you're now asking for distinct id's as well, and every id is distinct (by definition). Obviously I would rather see only one entry per category name.
I've already implemented a work around, which is to simply make the query above and then copy the results into an ArrayAdapter. My reason for this post is to see if there was a more elegant solution to this odd little problem and start a discussion on what I could be doing better. Alternate implementation suggestions, such as using different kinds of controls or adapters, are very welcome.
Here's the query I ended up with:
SELECT _id, category FROM table_name GROUP BY category;
I used the rawQuery() function on an SQLiteDatabase object to carry this out. The "GROUP BY" piece was the key towards getting the right results, so thanks to user Sagar for pointing me in the right direction.
Do consider user Al Sutton's answer to this question as well, as it may be a more elegant solution to this problem.
Thanks everyone!
I'd suggest having a separate table with just _id & category in it which contains one row per unique category. Your data rows can then replace their category field with the _id from the category table.
This has the added advantage you can change the category in the categories table and it will show up in all entries in that category.
SELECT DISTINCT category,_id FROM table GROUP BY category;
I think this should give you what you are looking for. The results from this will be the category, and the first _id for that category. You can ignore the second column (_id).
You can specify an _id field alias in your select statement that is just a constant value, for example:
SELECT DISTINCT 0 _id, category FROM table;
Better yet, I solved this problem by using:
SELECT DISTINCT category AS _id FROM table
Now, you have a column with the name _id which has what you want in it

Categories

Resources