I'm testing the foreign keys in Android and I have problems I don't understand:
To create the tables (with hard-coded values)
db.execSQL("CREATE TABLE IF NOT EXISTS table_A ( " +
"_id long primary key , value1 long );");
db.execSQL("CREATE TABLE IF NOT EXISTS table_B ( " +
"_id long primary key , value1fk long , value2 long,"+
"FOREIGN KEY (value1fk) REFERENCES table_A (value1) ON DELETE CASCADE);");
then I execute:
ContentValues values = new ContentValues();
values.put("_id", 1);
values.put("value1", 200);
long result = mDb.insert("table_A", null, values);
Log.e("","done (" + result + ")");
values = new ContentValues();
values.put("_id", 1);
values.put("value1fk", 200);
values.put("value2", 10);
result= mDb.insert("table_B", null, values);
Log.e("","done (" + result + ")");
The output is
done(1)
done(-1)
Giving the second insert an foreign key mismatch
E/SQLiteDatabase(25476): Error inserting _id=1 value1fk=200 value2=10
E/SQLiteDatabase(25476): android.database.sqlite.SQLiteException: foreign key mismatch: , while compiling: INSERT INTO table_B(_id,value1fk,value2) VALUES (?,?,?)
Why is that happening?
According to sqlite documentation on foreign keys
http://www.sqlite.org/foreignkeys.html
you need to either :
use the primary key as your foreign key
FOREIGN KEY (value1fk) REFERENCES table_A (_id)
use a Unique column as a foreign key
CREATE TABLE IF NOT EXISTS table_A (_id long primary key , value1 long UNIQUE);
The value_1 field in table_A is not the primary key. Shouldn't it be
FOREIGN KEY (value1fk) REFERENCES table_A (_id)...
instead?
Related
String Post_Code = "CREATE TABLE IF NOT EXISTS PostCode("
+ "PostCode_ID integer PRIMARY KEY, "
+ "Code string NOT NULL, "
+ "City_ID integer,"
+ "FOREIGN KEY (City_ID)"
+ "REFERENCES City (City_ID)"
+ "ON UPDATE CASCADE "
+ "ON DELETE SET NULL, "
+ "County_ID integer,"
+ "FOREIGN KEY (County_ID)"
+ "REFERENCES County (County_ID)"
+ "ON UPDATE CASCADE "
+ "ON DELETE SET NULL"
+ ");";
I believe it's most likely to be something within the first foreign key reference.
Either move all the FOREIGN KEY definitions at the end of the statement:
CREATE TABLE IF NOT EXISTS PostCode(
PostCode_ID integer PRIMARY KEY,
Code TEXT NOT NULL,
City_ID integer,
County_ID integer,
FOREIGN KEY (City_ID) REFERENCES City (City_ID) ON UPDATE CASCADE ON DELETE SET NULL,
FOREIGN KEY (County_ID)REFERENCES County (County_ID) ON UPDATE CASCADE ON DELETE SET NULL
);
or, define each foreign key right after the definition of each column without the FOREIGN KEY keywords:
CREATE TABLE IF NOT EXISTS PostCode(
PostCode_ID integer PRIMARY KEY,
Code TEXT NOT NULL,
City_ID integer REFERENCES City (City_ID) ON UPDATE CASCADE ON DELETE SET NULL,
County_ID integer REFERENCES County (County_ID) ON UPDATE CASCADE ON DELETE SET NULL
);
See the demo.
Note that there is no string data type in SQLite.
I changed it to TEXT.
I have tried to create three tables(CUSTOMERS, VEHICLES and RENTALS), the third table (RENTALS) has foreign keys referring to the two primary keys of the first two tables (CUSTOMERS and RENTALS). When creating this third table I get an error Missing columns in relationship(Rel=CUSTOMERS[[]] -> RENTALS[[]])
Here's my codes
private void createTables() throws SQLException {
Statement statement = conn.createStatement();
statement.executeUpdate("CREATE TABLE CUSTOMERS(custNumber AUTOINCREMENT PRIMARY KEY, " +
"firstName VARCHAR(155) NOT NULL, surname VARCHAR(155) NOT NULL, idNum INTEGER NOT NULL, phoneNum INTEGER NOT NULL, canRent BIT NOT NULL)");
statement.executeUpdate("CREATE TABLE VEHICLES(vehNumber AUTOINCREMENT PRIMARY KEY, make VARCHAR(155) NOT NULL, " +
"category VARCHAR(155) NOT NULL, rentalPrice FLOAT NOT NULL, availableForRent BIT NOT NULL)");
statement.executeUpdate("CREATE TABLE RENTALS(rentalNumber AUTOINCREMENT PRIMARY KEY, dateRental VARCHAR(155) NOT NULL, dateReturned VARCHAR(155) NOT NULL, " +
"pricePerDay FLOAT NOT NULL, totalRental FLOAT NOT NULL, custNumber INTEGER FOREIGN KEY REFERENCES CUSTOMERS(custNumber), " +
"vehNumber INTEGER FOREIGN KEY REFERENCES VEHICLES(vehNumber))");
System.out.println("Database populated");
}
and here's the error
Your help will be very much appreciated, I have looked around but found nothing that helps.
In Access, an AutoNumber field (DDL: AUTOINCREMENT or COUNTER) is a "Long Integer".
In UCanAccess DDL, INTEGER creates an "Integer" (16-bit) field and LONG creates a "Long Integer" (32-bit) field.
You need to declare your foreign key columns as LONG, not INTEGER.
I have this simple request :
SELECT *
FROM categories
LEFT OUTER JOIN items ON categories.id = items.category_id
ORDER BY items.category_id, items.name;
The problem is that the results are ordered following items.id, instead of the two specified columns.
If I replace with :
ORDER BY categories.id, items.name;
It works great, but it doesn't use my index:
CREATE INDEX items_index ON items(category_id, name);
How can I resolve that properly? Thank You.
Edit : Here is how I created the 2 tables :
// Categories
db.execSQL("CREATE TABLE categories (" +
"id INTEGER PRIMARY KEY, " +
"parent_id INTEGER , " +
"resource_name TEXT, " +
"FOREIGN KEY(parent_id) REFERENCES categories(id) ON UPDATE CASCADE ON DELETE CASCADE);");
db.execSQL("CREATE INDEX categories_index ON categories(parent_id);");
// Items
db.execSQL("CREATE TABLE items (" +
"id INTEGER PRIMARY KEY, " +
"category_id INTEGER , " +
"name TEXT, " +
"price REAL, " +
"FOREIGN KEY(category_id) REFERENCES categories(id) ON UPDATE CASCADE ON DELETE CASCADE);");
db.execSQL("CREATE INDEX items_index ON items(category_id, name);");
How I insert the categories :
ContentValues values = new ContentValues();
values.put("id", category.getId());
values.put("parent_id", category.getParentId());
values.put("resource_name", category.getResourceName());
db.insert("categories", null, values);
And the items :
ContentValues values = new ContentValues();
values.put("category_id", item.getCategoryId());
values.put("name", item.getName());
values.put("price", item.getPrice());
long id = db.insert("items", null, values);
I'm creating a table as follows:
#Override
public void onCreate(SQLiteDatabase db) {
String sql =
"CREATE TABLE IF NOT EXISTS users (" +
"_id INTEGER PRIMARY KEY, " +
"name TEXT NOT NULL, " +
"password TEXT NOT NULL);";
db.execSQL(sql);
sql = "INSERT INTO users VALUES ('testuser', 'textpassword')";
db.execSQL(sql);
}
But when I try to insert a user, I'm getting the following exception:
android.database.sqlite.SQLiteException: table users has 3 columns but 2 values were supplied (code 1): , while compiling: INSERT INTO users VALUES ('testuser', 'textpassword')
As _id is the primarykey, why is it expecting it? I also tried AUTOINCREMENT but it doesn't work.
Try this
#Override
public void onCreate(SQLiteDatabase db) {
String sql =
"CREATE TABLE IF NOT EXISTS users (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
"name TEXT NOT NULL, " +
"password TEXT NOT NULL);";
db.execSQL(sql);
sql = "INSERT INTO users(name, password) VALUES ('testuser', 'textpassword')";
db.execSQL(sql);
}
AUTOINCREMENT NOT NULL is missing in your insert query which will increase the value of _id field automatically.
You should specify the values you're inserting:
sql = "INSERT INTO users(name, password) VALUES ('testuser', 'textpassword')";
There's no need to specify AUTOINCREMENT: On an INSERT, if the ROWID or INTEGER PRIMARY KEY column is not explicitly given a value, then it will be filled automatically with an unused integer, usually one more than the largest ROWID currently in use. This is true regardless of whether or not the AUTOINCREMENT keyword is used.
I have a blob in my database table. If I select 10 rows of the table (each row containing 1 blob), it takes 300 milliseconds.
However, if I select 15 rows, it takes 20 seconds. I don't understand what is going on. The file that is selected is an image of 1 MB.
public void find() throws SQLException, IOException {
ResultSet rs = stm.executeQuery();
while (rs.next()) {
FileOutputStream output = new FileOutputStream(new File(
"C:\\Users\\test\\test.jpg"));
InputStream input = rs.getBinaryStream("photo");
byte[] buffer = new byte[8192];
int count = 0;
while ((count = input.read(buffer)) > 0) {
output.write(buffer, 0, count);
}
}
}
I honestly don't know why such small difference takes up so much time.
Any help is greatly appreciated.
Edit for extra notes:
Yes, the overwriting of the same image is intentional. (sorry for not pointing that out)
Using rs.getBlob("photo").getBinaryStream() has still the same effect.
The execution time of the method find() is being benchmarked via JMH. The piece of code above remains the same.
The exact same code and SQL is running for a 'contact' table that for the first test has 100 rows (of which 10 are selected) and the other 150 rows (of which 15 are selected).
The blob is inside the contact table.
The SQL:
String query = "SELECT * FROM contact c INNER JOIN contact_address ca ON c.id=ca.contact_id INNER JOIN groups_contact gc ON gc.contact_id=c.id INNER JOIN groups gr WHERE ca.country=? AND gr.name=? GROUP by c.id";
stm = conn.prepareStatement(query);
stm.setString(1, "NL");
stm.setString(2, "Friends");
stm.addBatch();
Update:
(measuring method is modified please see the above)
Selecting 10 rows: 239 ms.
Selecting 15 rows: 26378 ms.
Selecting 25 rows: 34888 ms.
Selecting 50 rows: 73267 ms.
Selecting 75 rows: 81885 ms.
Selecting 100 rows: 106528 ms.
Creations of table:
String createUserTable = "CREATE TABLE User (id INTEGER not NULL AUTO_INCREMENT, email VARCHAR(255), password VARCHAR(255), PRIMARY KEY (id))";
String createGroupTable = "CREATE TABLE Groups (id INTEGER not NULL AUTO_INCREMENT , name VARCHAR(255), user_id INTEGER not NULL, PRIMARY KEY (id), FOREIGN KEY (user_id) REFERENCES User(id))";
String createContactTable = "CREATE TABLE Contact (id INTEGER not NULL AUTO_INCREMENT , firstname VARCHAR(255), lastname VARCHAR(255), note VARCHAR(255), photo MEDIUMBLOB, user_id INTEGER, PRIMARY KEY (id), FOREIGN KEY (user_id) REFERENCES User(id))";
String createGroupContactTable = "CREATE TABLE Groups_Contact (id INTEGER not NULL AUTO_INCREMENT, contact_id INTEGER not NULL, group_id INTEGER not NULL, PRIMARY KEY (id), FOREIGN KEY (contact_id) REFERENCES Contact(id), FOREIGN KEY (group_id) REFERENCES Groups(id))";
String createContactAddressTable = "CREATE TABLE Contact_Address (id INTEGER not NULL AUTO_INCREMENT , street VARCHAR(255), number INTEGER, zipcode VARCHAR(255), city VARCHAR(255), country VARCHAR(255), addresstype VARCHAR(255), contact_id INTEGER not null, PRIMARY KEY (id), FOREIGN KEY (contact_id) REFERENCES Contact(id))";
String createContactPhoneTable = "CREATE TABLE Contact_Phone (id INTEGER not NULL AUTO_INCREMENT , type VARCHAR(255), number VARCHAR(255), contact_id INTEGER not null, PRIMARY KEY (id), FOREIGN KEY (contact_id) REFERENCES Contact(id))";
String createContactEmailTable = "CREATE TABLE Contact_Email (id INTEGER not NULL AUTO_INCREMENT , type VARCHAR(255), email VARCHAR(255), contact_id INTEGER not null, PRIMARY KEY (id), FOREIGN KEY (contact_id) REFERENCES Contact(id))";
Are you sure its not the joins? Some joins can be very slow, especially multiple joins. Often we had to run separate queries because joins might slow a query down.
Also what kind of indexes do you have?