I'm trying to build a database using 21 tables each bind with constraints. I have implemented the database in PHPmyAdmin and then download the structure script to import in my Android SQLite database.
But I would like to know what are the good practices in building relational databases with many tables in Android, and how to create them.
currently I have a class implementing SQLiteOpenHelper:
public class SqlSig extends SQLiteOpenHelper {
#Override
public void onCreate(SQLiteDatabase db) {
for(int i = 0; i != ConfigBDD.requetes.length; i++){
db.execSQL(ConfigBDD.requetes[i]);
}
}
}
ConfigBDD.requetes[i] fetch a string array containing tables creation requests and constraints requests. This code doesn't work at all, and logCat show an error with the PRIMARY KEY instruction:
05-29 15:13:51.992: E/Database(8187): Failure 1 (near "KEY": syntax error) on 0x15c4b8
when preparing 'CREATE TABLE IF NOT EXISTS `arret` ( `id` int(11) NOT NULL , `id_externe`
varchar(10) DEFAULT NULL, `id_pid` int(11) NOT NULL, `nom` varchar(50) NOT NULL,
`description` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `id_pid` (`id_pid`) )
DEFAULT CHARSET=utf8 =1 ;'.
I am pretty sure there are other ways to create this database but did not found any explanations about multi tables Android SQLite database creation...
Your:
CREATE TABLE IF NOT EXISTS `arret` (
`id` int(11) NOT NULL ,
`id_externe` varchar(10) DEFAULT NULL,
`id_pid` int(11) NOT NULL,
`nom` varchar(50) NOT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `id_pid` (`id_pid`))
DEFAULT CHARSET=utf8 =1 ;
should probably look something like this if you want it to work in SQLite.
CREATE TABLE IF NOT EXISTS arret (
id INTEGER PRIMARY KEY NOT NULL ,
id_externe TEXT DEFAULT NULL,
id_pid INTEGER NOT NULL,
nom TEXT NOT NULL,
description TEXT
);
followed by a CREATE INDEX for your id_pid which is what KEY implies in MySQL.
Related
I wonder what's wrong with my sql syntax.Its working on mysql but for sqlite gives me error.
CREATE TABLE IF NOT EXISTS `points` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET latin1 NOT NULL,
`longitude` double NOT NULL,
`latitude` double NOT NULL,
`radius` double NOT NULL,
`image` blob NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci AUTO_INCREMENT=4;
INSERT INTO `points` (`id`, `name`, `longitude`, `latitude`, `radius`, `image`)
VALUES (1, 'Isfahan, Iran', 51.678314, 32.65036, 0.064486,
0xffd8ffe000104a46494600010001009600960000fffe001f4c45414420546563686e6f6c6f6769657320496e632e2056312e303100ffdb008400100b0c0e0c0a100e0d0e1211101318281a181616183123251d283b343e3d3a34393841495d4f414558463839516f52586063696a693f4e737b72667a5d676964011112121815182f1a1a2f644339436464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464ffc4003000000203010000000000000000000000000001020304050101010101010100000000000000000000000102030405ffc20011
It should be AUTOINCREMENT as one word.
You dont require this statement: ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci AUTO_INCREMENT=4; which is MySQL specific. Further more, if you are setting the autoincrement value to start from 5, then you shouldn't be inserting an id of value equal to 1.
You do not seem to have a closing bracket after the insert statement.
You should instead try:
CREATE TABLE IF NOT EXISTS `points` (
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
`name` varchar(50) CHARACTER SET latin1 NOT NULL,
`longitude` double NOT NULL,
`latitude` double NOT NULL,
`radius` double NOT NULL,
`image` blob NOT NULL
);
INSERT INTO `points` (`id`, `name`, `longitude`, `latitude`, `radius`, `image`)
VALUES (1, 'Isfahan, Iran', 51.678314, 32.65036, 0.064486,
0xffd8ffe000104a46494600010001009600960000fffe001f4c45414420546563686e6f6c6f6769657320496e632e2056312e303100ffdb008400100b0c0e0c0a100e0d0e1211101318281a181616183123251d283b343e3d3a34393841495d4f414558463839516f52586063696a693f4e737b72667a5d676964011112121815182f1a1a2f644339436464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464ffc4003000000203010000000000000000000000000001020304050101010101010100000000000000000000000102030405ffc20011);
There could be more fixes required as well. Back tick though recognized by SQLite, is a MySQL specific quoting.
I havea weird problem in my android application which is related to database relations on foreign keys. The following codes describe my simple database's structure,
i have used SQLiteOpenHelper as a super class for handle database operation
private static final String CATEGORIES_TABLE = "CREATE TABLE __CATEGORIES_TBL(_id INTEGER PRIMARY KEY AUTOINCREMENT, _name TEXT NOT NULL, _desc TEXT NULL,"
+ "_create_date INTEGER NOT NULL, _update_date INTEGER NULL"
+ ", _parent_id INTEGER NULL, FOREIGN KEY(_parent_id) REFERENCES __CATEGORIES_TBL(_id) ON DELETE RESTRICT);";
private static final String CARDS_TABLE = "CREATE TABLE __CARDS_TBL(_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "_value TEXT NOT NULL, _play_count INTEGER NULL, "
+ "_category_id INTEGER NOT NULL, FOREIGN KEY(_category_id) REFERENCES __CATEGORIES_TBL(_id) ON DELETE RESTRICT);";
#Override
public void onCreate(SQLiteDatabase db) {
try {
db.execSQL("PRAGMA foreign_keys = ON;");
db.execSQL(CATEGORIES_TABLE);
db.execSQL(CARDS_TABLE);
Logger.i("DB-INIT-DONE!");
} catch (Exception ex) {
Logger.e("Database on create error", ex);
}
}
as you see everything seems to be O.K and it is; I can insert, edit , select row(s) to/from both tables but unfortunately i can delete rows which they have child rows.
as i expected. because i set the FK (foreign-key) relation between the tow tables with ON DELETE RESTRICT mode therefore i expect to get an exception when i try to delete a row from parent table (__CATEGORIES_TBL) , actually the parent record is deleting and no exception happens,
by theory sqlite must prevent deleting any row in __CATEGORIES_TBL when it has one or more child row(s) in __CARDS_TBL or any child row(s) in __CATEGORIES_TBL but in my application i can delete rows when it has a parent-child relationship rows,
consider the following code (this is the deleting code)
private SQLiteDatabase db;
public long delete(long objId) {
try {
// TABLE_NAME can be __CATEGORIES_TBL or __CARDS_TBL based on program flow
return db.delete(TABLE_NAME, COLUMN_ROWID + "=" + objId, null);
} catch (Exception e) {
Logger.d("Unable to delete category <" + objId + ">.", e);
return -123456;
}
}
every call to db.delete returns 1 (means 1 row is deleted by this command) this code is executing under android 2.3 ;
Thanks in advance.
I get the behavior I'd expect from SQLite 3.7.9.
sqlite> CREATE TABLE __CATEGORIES_TBL (
...> _id INTEGER PRIMARY KEY AUTOINCREMENT,
...> _name TEXT NOT NULL,
...> _desc TEXT NULL,
...> _create_date INTEGER NOT NULL,
...> _update_date INTEGER NULL,
...> _parent_id INTEGER NULL,
...> FOREIGN KEY(_parent_id)
...> REFERENCES __CATEGORIES_TBL(_id) ON DELETE RESTRICT
...> );
sqlite>
sqlite> CREATE TABLE __CARDS_TBL(
...> _id INTEGER PRIMARY KEY AUTOINCREMENT,
...> _value TEXT NOT NULL,
...> _play_count INTEGER NULL,
...> _category_id INTEGER NOT NULL,
...> FOREIGN KEY(_category_id)
...> REFERENCES __CATEGORIES_TBL(_id) ON DELETE RESTRICT
...> );
sqlite>
sqlite> insert into __categories_tbl values
...> (1, 'name', 'desc',current_date,current_date,1);
sqlite> insert into __cards_tbl values (1, 'value',3, 1);
sqlite> pragma foreign_keys=on;
sqlite> select * from __categories_tbl;
1|name|desc|2012-08-25|2012-08-25|1
sqlite> delete from __categories_tbl;
Error: foreign key constraint failed
If I were you, I'd try to visually inspect the SQLite database after each step to see whether what you expect to happen is actually happening. IIRC, not every failure will raise an exception.
Foreign key support was introduced in version 3.6.19. There are compile-time settings that allow SQLite to parse foreign key constraints, but don't allow it to actually enforce them. (SQLITE_OMIT_TRIGGER defined, SQLITE_OMIT_FOREIGN_KEY not defined.) You should be able to tell whether your build of SQLite can enforce foreign keys by trying to create a trigger.
If creating a trigger fails with a parse error, then SQLite will parse foreign key statements, but won't enforce them. You'll need to recompile your build of 3.6.19, or upgrade to a newer version. (And check those settings before you compile.)
A column declared NOT NULL can appear to be empty if you've inserted an empty string. By default, NULL and empty strings look identical on output. You can tell whether a column contains nulls by selecting them.
select * from table_name where column_name is null;
about this problem i have put a db.execSQL("pragma foreign_keys=on;"); before delete statement in my app code then the problem solved, but i think this is unnecessary code maybe my phone's installed sqlite configs are wrong and needs to be reconfigured , is it possible to reconfigure the sqlite on phone ?
the test device is a HTC wildfire s (running sqlite version is 3.7.2)
I'm creating an app as a learning tool and am having difficulty with join queries.
I have a database with two tables- horses and covers- declared as follows;
private static final String HORSES_CREATE = "create table horses (_id integer primary key autoincrement, "
+ "name text not null, type integer not null, birthDate text not null, vaccineDate text not null, "
+ "inFoal integer not null, notes text not null);";
The 'type' field refers to stallion, mare, gelding etc and is selected from a spinner (populated from an XML String array).
private static final String COVERS_CREATE = "create table covers (_id integer primary key autoincrement, "
+ "stallionName integer not null, mareName integer not null, firstCoverDate text not null, lastCoverDate text not null, "
+ "scan14Date text not null, scan28Date text not null, foalingDate text not null, inFoal integer not null, notes text not null);";
stallionName is actually stored as the _id field of the horse from the horse table. It is selected from a spinner that only displays horses whose type defined as 'Stallion' in the horses table. (The same applies for Mare).
I have a class 'DatabaseHelper' to create and upgrade the tables, and each table has its own adapter class 'horsesDbAdapter' and 'coversDbAdapter' that contains the methods to add, edit and delete entries, and relevant queries. (fetchAllHorses(), fetchHorse(long rowId) )
eg:
public Cursor fetchAllHorses() {
return mDb.query(DATABASE_TABLE,
new String[] { KEY_ROWID, KEY_NAME, KEY_TYPE, KEY_BIRTHDATE,
KEY_VACCINEDATE, KEY_INFOAL, KEY_NOTES }, null, null,
null, null, null);
}
(It's all adapted from the Android notepad example)
I have the contents of the covers table displayed in a listview (just showing the stallionName and mareName). But as those fields just contain the unique reference to the horses table all that is displayed is the fairly uninformative _id field.
My question is; how can I get the relevant name for the horses to display in the listView? I've read up on join queries etc but get lost when I try implement them. I assume I have to join on horses._id and covers.stallionName (then make an almost-identical one for MareName) but I can't find a concrete example of how to do this.
Please let me know if any additional information/ code is needed.
Any help would be greatly appreciated, thankyou in advance.
EDIT:
I have made stallionName and mareName foreign keys referencing (_id) in the horses table, but am still unsure how and where to implement the join query; should it be in the horsesDbAdapter, coversDbAdapter or the coversList class? (coversList is the class that creates and populates the listView)
The covers table declaration now reads;
private static final String COVERS_CREATE = "create table covers (_id integer primary key autoincrement, "
+ "stallionName integer not null, mareName integer not null, firstCoverDate text not null, lastCoverDate text not null, "
+ "scan14Date text not null, scan28Date text not null, foalingDate text not null, inFoal integer not null, notes text not null," +
"FOREIGN KEY (stallionName) REFERENCES horses (_id), FOREIGN KEY (mareName) REFERENCES horses (_id));";
I'm a newbie to Android and SQLLiteHelper and was wondering about the same thing.
After reading this post, I found that the method to define joins between tables is done with the SQLLiteQueryBuilder.setTables method. See the reference here
I got this from this blog
Hope this helps pointing readers in the right direction.
I've managed to get it working. For anyone else who may have a similar problem I'll try detail what I did.
As #deceiver stated I should have made stallionName and mareName foreign keys referencing horses (see Edit).
In the coversList class (the class that implements the listView) I just needed to get an instance of the database and use a rawQuery to implement the SQL code directly (It may be possible to do it with Query but I'm not sure how)
The added code is as follows;
private Cursor coversCursor;
private void fillData() {
db = (new DatabaseHelper(this).getReadableDatabase());
coversCursor = db.rawQuery("SELECT horses1.name AS stallionNameText, covers.*, horses2.name AS mareNameText FROM horses horses1 JOIN covers ON horses1._id = covers.stallionName JOIN horses horses2 ON horses2._id = covers.MareName",
null);
startManagingCursor(coversCursor);
// Create an array to specify the fields we want to display in the list
// (the renamed fields from the above query)
String[] from = new String[] { "stallionNameText", "mareNameText" };
// and an array of the fields we want to bind those fields to (in this
// case just text1)
int[] to = new int[] { R.id.rowStallionName, R.id.rowMareName };
// Now create a simple cursor adapter and set it to display
SimpleCursorAdapter covers = new SimpleCursorAdapter(this,
R.layout.covers_list_row, coversCursor, from, to);
setListAdapter(covers);
}
FillData() is then called in the onCreate() method. Then just close the db in the onDestroy method.
(Selecting all columns from the covers table seems wasteful here but I will eventually show these columns in the listView aswell. I just wanted to answer this before continuing coding).
A tutorial I found helpful was http://coenraets.org/blog/android-samples/androidtutorial/
I had issues with the SQL query as there are 2 foreign keys referencing the same table and I wanted the listView to display both the stallion name and mare name, so had to join two horses table to a covers table. I just needed to rename the tables in the FROM section of the SQL query. Hopefully the above code is clear. If not, I found the following useful; http://www.bryantwebconsulting.com/blog/index.cfm/2005/3/11/join_a_table_to_itself_in_sql
Sorry if this explanation is too specific to my (unusual) example.
Thanks for reading.
I'm creating a dataBase to insert a *unique patient *(no more than one), so I just created a database that doesn't autoincrement its id like this:
CREATE TABLE IF NOT EXISTS PACIENTE(idPaciente INTEGER PRIMARY KEY, nombre VARCHAR(100) NOT NULL, apellidos VARCHAR(100), email VARCHAR(100), genero CHAR, edad INTEGER NOT NULL, peso INTEGER, kMedico INTEGER, pkHistorial INTEGER, pkConfProg INTEGER, altura INTEGER, FOREIGN KEY (pkMedico) REFERENCES MEDICO(idMedico), FOREIGN KEY (pkHistorial) REFERENCES HISTORIAL(idHistorial), FOREIGn KEY (pkConfProg) REFERENCES CONFPROGRAMA(idConf));
As you can see, the way to add a patient here is tell the database the idPaciente explicitly.
So I used this code to insert a patient:
public long addPaciente(BDPaciente pac)
{
ContentValues cv = new ContentValues();
cv.put("idPaciente", 1);
cv.put("nombre", pac.getNombre());
cv.put("edad", 26);
try
{
db.insert("PACIENTE", null, cv);
return -1;
}
catch (SQLiteConstraintException e)
{
return -100;
}
}
As you can see, what I'm trying to do is insert a patient, and then, if it is inserted before, catch the Exception and throw it to my parent Window. The thing is that, the exception is thrown, but not catched. And the program says:
Error inserting nombre=blabla edad=25 idPaciente=1
android.database.SQLiteConstraintException: error code 19: constraint failed
I know that it's something about the duplication on the primary key, but I wanna do so!
Flo, thanky you for your answer, but yes, I created the table, but what I didn't post, is that I have a method for erasing all databases and then creating them again whenever I press a button like this:
db.execSQL(DROP_TABLE_HISTORIAL);
db.execSQL(DROP_TABLE_MEDICO);
db.execSQL(DROP_TABLE_CONF);
db.execSQL(DROP_TABLE_PACIENTE);
So yes, I'm sure. But what Sarmand answered works for me, so thank you for your help :-)
Sorry, but I can't vote... Don't have enough points >_<
If you want to duplicate primary key then dont declare it as primary key. Do it like this
CREATE TABLE IF NOT EXISTS PACIENTE(idPaciente INTEGER , ....);
Im getting this error when i try to access my View
I've built my database/View using this
CREATE TABLE Boxer(
BoxerId INTEGER PRIMARY KEY AUTOINCREMENT,
Firstname NVARCHAR(50) NOT NULL,
Lastname NVARCHAR(50) NOT NULL
);
CREATE TABLE Match(
MatchId INTEGER PRIMARY KEY AUTOINCREMENT,
BoxerA INTEGER NOT NULL FOREIGN KEY REFERENCES Boxer(BoxerId),
BoxerB INTEGER NOT NULL FOREIGN KEY REFERENCES Boxer(BoxerId),
MatchDate date NOT NULL DEFAULT GETDATE(),
NumberOfRounds INTEGER NOT NULL DEFAULT 12
);
CREATE TABLE Round(
RoundId INTEGER PRIMARY KEY AUTOINCREMENT,
MatchId INTEGER NOT NULL FOREIGN KEY REFERENCES Match(MatchId),
BoxerA INTEGER NOT NULL DEFAULT 0,
BoxerB INTEGER NOT NULL DEFAULT 0,
Position INTEGER NOT NULL
);
/*
Building a view which dislpays matches with boxers names and total scores
*/
CREATE VIEW MatchDetail AS
SELECT Match.MatchId, A.BoxerId AS IdA, B.BoxerId AS IdB, A.Firstname + ' ' + A.Lastname AS NameA, B.Firstname + ' ' + B.Lastname AS NameB,
(SELECT SUM(R.BoxerA) AS Score FROM Round AS R WHERE (R.MatchId = Match.MatchId)) AS ScoreA,
(SELECT SUM(R.BoxerB) AS Score FROM Round AS R WHERE (R.MatchId = Match.MatchId)) AS ScoreB,
Match.MatchDate, Match.NumberOfRounds
FROM Boxer AS A INNER JOIN Match ON A.BoxerId = Match.BoxerA INNER JOIN Boxer AS B ON Match.BoxerB = B.BoxerId
I've pretty much built my app so far using the notepad example so I then call my DbHelper
Cursor MatchesCursor = mDbHelper.fetchAllMatchDetails();
This then calls the query
public Cursor fetchAllMatchDetails(){
return mDb.query(VIEW_MATCHDETAIL, new String[] {
"MatchId"
}, null, null, null, null, null);
}
VIEW_MATCHDETAIL is defined as a string = "MatchDetail"
and it's here where it crashes saying
no such table MatchDetail: while compiling SELECT MatchId FROM MatchDetail
anyone had this problem before?
You have some beautiful SQL there. Unfortunately only the first line of sql will be executed in SQLiteDatabase.execSQL. The rest will be ignored silently (convenient eh?). Split up the statements manually like this:
https://github.com/browep/fpt/blob/master/src/com/github/browep/nosql/NoSqlSqliteOpener.java
or if you like to keep your sql in a separate file, try this:
String sqlText = getSqlText();
for(String sqlStmt : sqlText.split(";"))
myDb.execSQL(slqStmt + ";");
What stands out to me is the use of datatypes like NVARCHAR(50). SQLite only has a very simple set of datatypes. I'm surprised it doesn't throw an exception when you install the app. Try using simply TEXT instead.
If you cannot access a database that you know you have initialized, try passing the Context from the Activity that created the table to the class trying to query the table. Use that Context as part of your connection initialization.