Im receiving throught BLE data stored in an SD Card. This data is organized in multiple text files, with each file corresponding to a date.
When receiving this data on android i want to save it on a SQlite database.
Thought about using the same logic, creating a table for each day. My question is if its possible to automatically create tables depending on the number of days that is going to be transfered. After some research i found how to add new tables using the onUpgrade method and changing the database version, but this seems only possible by changing the database version manually.
Another option would be by creating a single table for all the data, and add the date as a column.
Any feedback is valuable!
Typically you would use a single table with the date as a column.
It would be possible to dynamically create tables, if they don't exist outside of the onUpgrade method. For each date/file you could, when receiving the file and before loading/inserting the data, either :-
use CREATE TABLE IF NOT EXISTS the_table_with_a_name_that_relates_to_the_date (the_column_definitions)
i.e. if the table exists then the above is effectively a NOOP.
use something like (the below assumes this method is in the DatabaseHelper)
:-
public bolean checkAndAddTable(String tableName) {
boolean rv = false;
SQLiteDatabase = this.getWriteableDatabase();
Cursor csr = db.query("sqlite_master",null,"name=? AND type='table'",new String[]{tableName},null,null,null);
if (csr.getCount() < 1) {
db.execSQL("CREATE TABLE IF NOT EXISTS " + tableName + "(......SQL TO CREATE THE COLUMN DEFINITIONS......)");
rv = true;
}
csr.close();
return rv;
}
Note the code is in-principle code ans has not been run or tested and my therefore have some errors.
However, extracting the data from multiple tables would/should need to check if the table exists, to see if data can be extracted which would incur additional processing/complications (e.g. what to do if it doesn't exist).
Related
I have an app released in app store
I want to add a new column to the user table in the sqlite db and want it to be not null
But I also want old users to be able to use the app
What will happen if an old user updates their app with the new version? At login, I get the info from the server and I insert it in the db. The db insertion will probably stop when there is no value for the new column
Also, how do I do the upgrade itself?
in onUpgrade I do "ALTER table USER..."
in the constructor of the SQLite helper I add the new DB version
what else?
Also, I've added 3-4 totally new tables needed for new features. I've called their create queries in the onCreate method of the SQLite helper. Should I do anything else in addition to this?
in the constructor of the SQLite helper I add the new DB version
That will trigger onUpgrade(), good.
However, you cannot use ALTER TABLE to add a column with NOT NULL.
Here's what you can do in onUpgrade() to preserve user data:
Rename the old table to a temporary name
Recreate the table with the new column and NOT NULL
Populate the new table from the old temp table and supply the new column a reasonable non-null default value
Drop the temporary table
Can you give me an example lets say table is called USER with fields NAME and EMAIL and now I want to add a new field AGE?
Here's an example:
sqlite> create table user(name, email);
sqlite> insert into user select 'foo','bar';
sqlite> alter table user rename to user_temp;
sqlite> create table user(name, email, age not null);
sqlite> insert into user select name,email,-1 from user_temp;
sqlite> drop table user_temp;
sqlite> select * from user;
name|email|age
foo|bar|-1
Also, I've added 3-4 totally new tables needed for new features. I've called their create queries in the onCreate method of the SQLite helper. Should I do anything else in addition to this?
should I put the new creates for the new tables in both onCreate and onUpgrade or only in onUpgrade?
Make sure the same new tables are created in onUpgrade().
onCreate() is only run when the database is created for the first time, not on upgrade.
After both onCreate() and onUpgrade() the database schema (table structure) should be compatible. How you implement it is up to you. Putting the CREATE TABLEs there in onUpgrade() is an option. Some people prefer to call onCreate() insinde onUpgrade(), which can cause some headache when trying to migrate old data.
I am on Android and this question is of Sqlite :
I have a table (USERLOGIN) which holds the user's credentials (username and password) in it. I have another table ($USER$_PROJS) which holds information of projects for a particular user. Users can create and add projects in there.
[ $USER$ will be a variable which comes from the column username in USERLOGIN table. So its basically a dynamic table creation. ]
Both of these tables are in the same database USER.db.
I have only one LoginDatabseAdapter class and DatabseHelper class which manages both of these.
Now initially when user logs-in, the database is behaving properly. But when inside the user profile when a user tries to create/add project its not inserting the values into $USER$_PROJS table.
I think i need to use the USE TABLE (once the user successfully logs-in his profile) like statement but its giving an error when i try to use it.
I have searched almost all the resources on net but was unable to find the solution !!
Any help would be appreciated !!
CODE RESPONSIBLE FOR CREATING TABLE :
public void createprojtable(String u){
db.execSQL(
"create table if not exists "+u+"_PROJS(ID integer primary key autoincrement,
PROJ_NAME text,DATE text); ");
}
public void insertProjEntry(String u,String projName,
String projdate) {
//db.execSQL("use table "+u+"_PROJS;");
ContentValues newValues = new ContentValues();
newValues.put("PROJ", projName);
newValues.put("DATE", projdate);
db.insert(u+"_PROJS", null, newValues);
}
Have a look at the Cursors for Sqlite in Android. You get Cursors specific to a table. Using the Cursors you can read data from a specific table as such. While updating the data also you can specify table name as one of the parameters. So you can pretty much get what you are looking for using existing APIs.
Have a look at following links :
Android SQLite Database and ContentProvider - Tutorial
android.database.sqlite
I am using a SQLite database to store data that can be used to reconstruct some objects that I am using in the application I am developing. I am storing CheckIns, Recipients, and ContactMethods.
These objects are related as follows:
CheckIn <--many -- to -- many--> Recipient
Recipient <--one -- to -- many--> ContactMethod
In Java, these objects' fields are defined as follows:
public class CheckIn {
private int id;
private boolean isEnabled;
private Date startTime;
private Repetition repetition;
private Set<Recipient> recipients;
}
public class Recipient {
private String name;
private Set<ContactMethod> contactMethods;
}
public class ContactMethod {
private String type;
private String address;
}
The database schema I have come up with for these objects is defined as follows:
CREATE TABLE checkIn(
_id INTEGER PRIMARY KEY AUTOINCREMENT,
isEnabled INTEGER,
startTime INTEGER,
repetitionNum INTEGER,
repetitionUnits TEXT
);
CREATE TABLE recipient(
_id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT
);
CREATE TABLE contactMethod(
_id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT,
address TEXT,
recipientID INTEGER,
FOREIGN KEY(recipientID) REFERENCES recipient(_id) ON DELETE CASCADE
);
CREATE TABLE checkIn_recipient(
checkInID INTEGER,
recipientID INTEGER,
FOREIGN KEY(checkInID) REFERENCES checkIn(_id),
FOREIGN KEY(recipientID) REFERENCES recipient(_id)
);
I have two questions.
1. How do I efficiently INSERT a CheckIn to the database, along with its related objects?
To be more specific, if I have a CheckIn object in Java, not yet committed to the database, how can I structure an INSERT statement that will insert the CheckIn to the CheckIn table, but also store the new CheckIn's relation to one or more Recipients? It seems like to store the relation to the Recipients, I would need to already know the checkIn._id, which hasn't been set yet, since the CheckIn hasn't been entered into the database yet.
2. In Android, what is the best way to rebuild a CheckIn object, for example, from a database query?
I think I know the SQL query that I will need to get the right data:
SELECT checkIn.*, recipient.name, contactMethod.type, contactMethod.address
FROM checkIn
JOIN checkIn_recipient
ON (checkIn._id = checkIn_recipient.checkInID)
JOIN recipient
ON (checkIn_recipient.recipientID = recipient._id)
JOIN contactMethod
ON (recipient._id = contactMethod.recipientID)
This query will get me rows of data containing all of the information I need to build an object for every CheckIn and Recipient in the database, and I know how to get a Cursor object that will iterate through these rows. However, since the data required for a single CheckIn appears on multiple rows, I am confused about the best way to construct individual CheckIn objects. If I am trying to write a method like public Set<CheckIn> getAllCheckIns() that will return a set of all CheckIns that are stored in the database, do I need to run the query above, then loop through each row with the same checkIn._id, and within that loop, every row with the same recipient._id, and so forth? Is there any better way to do this?
I am sorry for the long question. I have only been working with SQL beyond single table databases since this morning, and I didn't want to leave out any important information. Let me know if I missed something.
Answer to Question 1: There are 2 possible ways.
a. You find the ID of the inserted row and use that to insert into the 2nd table. You can find the ID of inserted row if you are using the Android Insert method as documented here:
http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#insert%28java.lang.String,%20java.lang.String,%20android.content.ContentValues%29
Here you must ensure that all DB tables are committed or none.
b. You can create triggers. Triggers are database operations that are automatically performed when a specified database event occurs.
Learn how to create triggers: http://www.sqlite.org/lang_createtrigger.html
So for e.g. you can create a AFTER INSERT trigger that will be fired when a row in inserted in check in table. Then in that trigger you can insert a row into another table.
When the trigger is fired, you have access to the newly inserted row.
Answer to question 2:
I usually do the following :
Select from table 1 - Check In table
Iterate over the cursor and prepare the Check In object.
Within the loop, select from table 2 - Recipient table
Iterate over the recipient table and prepare the Check in object.
This would involve too many DB selects.
Alternatively, you could select all data once and then iterate to prepare the objects.
The point I am trying to make is that you have to iterate :D
I have a database with multiple tables. One of these tables (sport) is where i have to put a static list of object, each one with an _id, name, logo and an int. The _id will be used by other tables to do some queries (eg. select from "table X" where sport_id = _id), so it shouldn't change overtime (is there a way to update all the reference to this _id if it will change?).
Where should i put the code (i think it will be a simple list of db.insertSport()) to make it add this row only one time (and check if the row number grow, to add the new ones)?
There won't be much row, 50 at the best.
I think I would make a method in the dbHelper to insert that data, then call that method immediately upon app start. I'm making a couple of assumptions here... first that you are shipping this static info with the app and when you want to add more info you will be shipping a new version.
You could store the data as a text file in your assets folder and then read the file in execute a batch insert in the method.
If you set it up right (use insertWithOnConflict and the CONFLICT_IGNORE flag in the method) it will only add the new rows (if any) each time so you can run it every time the app starts and not worry about duplicate data or crashes for constraint violations.
If you only want it to run the once and then again when there is additional info, put a version number in the text file and check that against the previous one (which you can store in SharedPreferences).
EDIT
Example of using insertWithOnConflict:
public long createItem(String yourdata) {
ContentValues initialValues = new ContentValues();
initialValues.put(YOUR_COLUMN, yourdata);
return mDb.insertWithOnConflict(YOUR_TABLE, null, initialValues,
SQLiteDatabase.CONFLICT_IGNORE);
}
You can read up on the SQLiteDatabase class (which has the constants and methods) here
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.