I want to connect to tables in my database with different ID's (CustomerID, OrderID). I use the following code to create my foreign key:
FOREIGN KEY("+ COLUMN_ORDER_ID + ") REFERENCES+TABLE_NAME_CUSTOMER + "(" + COLUMN_CUSTOMER_ID + "));";
I'm not posting the complete source code, because my first table works fine and I think the problem is the foreign key.
I filter my data with this method:
public List<Orders> getOrdersByCustomerID() {
List<Orders> orderList = new ArrayList<Orders>();
String query = "select " + COLUMN_ORDER_ID
+ "," + COLUMN_ORDER_NAME
+ "," + COLUMN_SETS
+ "," + COLUMN_REPEATS
+ "," + COLUMN_SECTION
+ " from " + TABLE_NAME_ORDERS
+ " where " + COLUMN_CUSTOMER_ID
+ "=" + COLUMN_ORDER_ID;
db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(query, null);
if (cursor.moveToFirst()) {
do {
Orders orders = new Orders(cursor.getString(0), cursor.getString(1), cursor.getInt(2), cursor.getInt(3), cursor.getString(4));
orderList.add(orders);
} while (cursor.moveToNext());
}
db.close();
return orderList;
}
I get this error message:
06-26 17:11:26.154 10163-10163/com.xxx.xxx.xxx
E/AndroidRuntime: FATAL EXCEPTION: main process: com.xxx.xxx.xxx, PID: 10163
java.lang.RuntimeException: Unable to start activity
android.database.sqlite.SQLiteException: no such column:
customerId(Sqlite code 1): , while compiling: select
orderId,orderName,sets,repeats,section from orders where
customerId=orderId,(OS error - 2:No such file or directory)
I think the connection between this two ids is incorrect, or must I commit the id from my customer table? Any tips? As per my understanding, a Customer can have multiple Orders. That's why I use the foreign key; I hope this is correct.
Edit: I was wrong apparently queries are case-insensitive by default in mysql.
Edit:
In order to create a foreign key both tables must contain the ConsumerId. This allows you the establish a relationship using the foreign key.
Your query has to be changed as wel, after creating the foreign key. It should be something like this
select " + COLUMN_ORDER_IDq
+ "," + COLUMN_ORDER_NAME
+ "," + COLUMN_SETS
+ "," + COLUMN_REPEATS
+ "," + COLUMN_SECTION
+ " from " + TABLE_NAME_ORDERS + "," + TABLE_NAME_CUSTOMERS
+ " where " + TABLE_NAME_ORDERS +"." + COLUMN_CUSTOMER_ID
+ "=" + TABLE_NAME_CUSTOMERS + "." + COLUMN_CUSTOMER_ID
Related
I have created the following table:
"CREATE TABLE ParsonCollection "
+ "(id integer not null GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),"
+ "name varchar(20),"
+ "eye varchar(20),"
+ "hair varchar(20),"
+ "height varchar(20),"
+ "weight varchar(20),"
+ "PRIMARY KEY (id))";
I am then trying to insert into the table and this is where I am running into issues. When I try to alter the "id" column, I get an error saying "java.sql.SQLSyntaxErrorException: Attempt to modify an identity column 'ID'. " Here is what the insert statement looks like:
"insert into ParsonCollection values(" + q_surround(Name) + ","
+ q_surround(Eye) + "," + q_surround(Hair) + "," + q_surround(Height) + "," + q_surround(Weight) + ",1" + ")";
However, when I take away the field that is inserting into "id", I get the following error: "java.sql.SQLSyntaxErrorException: The number of values assigned is not the same as the number of specified or implied columns." Here is what this insert statement looks like:
"insert into ParsonCollection values(" + q_surround(Name) + ","
+ q_surround(Eye) + "," + q_surround(Hair) + "," + q_surround(Height) + "," + q_surround(Weight) + ")";
How do I get past this? It seems that when I solve one exception, the other one pops up and vice versa. Thanks.
You can't assign to an identity column. Since you cannot pass all values for insert, you need to enumerate the columns (omitting the identity column):
"insert into ParsonCollection (
name,
eye,
hair,
height
weight
) values("
+ q_surround(Name)
+ "," + q_surround(Eye)
+ "," + q_surround(Hair)
+ "," + q_surround(Height)
+ "," + q_surround(Weight)
+ ")";
Side note: your code is opened to SQL injection. You should seriously consider using prepared statements and bind parameters instead of concatenating the query string.
Out of interest, another solution would be to make the identity column IMPLICITLY HIDDEN
The task I've been given is to write a query to return the number of orphaned rows. I have achieved this but another task is to then not use the rawQuery method to achieve the same result using the query method.
The issue is that I get java.lang.IllegalStateException: Invalid tables
The tables, there are 3 are
the parent table which has an _id column and a name column
the child table which has an _id column, a name column and a childtoparentlink column that is an integer that links to the parent table.
the friend table which has an _id column, a name column and a friendtochildlink column.
The SQL to create and to put rows into the tables, including some orphans is like
CREATE TABLE parent(_id INTEGER PRIMARY KEY,parentname TEXT);
CREATE TABLE child(_id INTEGER PRIMARY KEY,childname TEXT, childtoparentlink INTEGER);
CREATE TABLE friend(_id INTEGER PRIMARY KEY,friendname TEXT, friendtochildlink INTEGER);
INSERT INTO parent VALUES(null,'Parent A');
INSERT INTO parent VALUES(null,'Parent B');
INSERT INTO child VALUES(null,'Child A',1);
INSERT INTO child VALUES(null,'Child B',2);
INSERT INTO child VALUES(null,'Child X',10); -- orphan
INSERT INTO friend VALUES(null,'Friend A',1);
INSERT INTO friend VALUES(null,'Friend B',2);
INSERT INTO friend VALUES(null,'Friend X',100); -- orphan
The query that works and gives the right values when using rawQuery is
SELECT
(
SELECT count() FROM child
LEFT JOIN parent ON child.childtoparentlink = parent._id
WHERE parent.parentname IS NULL
) AS child_mismatches,
(
SELECT count() FROM friend
LEFT JOIN child ON friend.friendtochildlink = child._id
WHERE child.childname IS NULL
) AS friend_mismatches
I get two columns each with a value of 1 (as wanted).
My actual code is :-
public ArrayList<String> checkLinkIntegrity() {
ArrayList<String> return_value = new ArrayList<>();
String suffix = "_mismatches";
String child_result_cl = TB_CHILD + suffix;
String sq_child_mismatches = "(SELECT count() FROM " +
TB_CHILD +
" LEFT JOIN " + TB_PARENT +
" ON " + TB_CHILD + "." + CL_CHILDTOPARENTLINK + " = " +
TB_PARENT + "." + CL_PARENTID +
" WHERE " + TB_PARENT + "." + CL_PARENTNAME + " IS NULL)" +
" AS " + child_result_cl;
String friend_result_cl = TB_FRIEND + suffix;
String sq_friend_mismatches = "(SELECT count() FROM " +
TB_FRIEND +
" LEFT JOIN " + TB_CHILD +
" ON " + TB_FRIEND + "." + CL_FRIENDTOCHILDLINK + " = " +
TB_CHILD + "." + CL_CHILD_ID +
" WHERE " + TB_CHILD + "." + CL_CHILDNAME + " IS NULL)" +
" AS " + friend_result_cl;
String full_query = "SELECT " + sq_child_mismatches + "," + sq_friend_mismatches;
SQLiteDatabase db = this.getWritableDatabase();
Cursor csr;
Log.d("RAWQUERYSQL",full_query);
csr = db.rawQuery(full_query,null);
return_value.addAll(dumpCursorToStringArrayList(csr,"RAWQUERY"));
// Fails invalid table
csr = db.query(null,new String[]{sq_child_mismatches,sq_friend_mismatches},null,null,null,null,null);
return_value.addAll(dumpCursorToStringArrayList(csr,"SECONDTRY"));
csr.close();
return return_value;
}
and the dumpCursortoStringArrayList method is :-
private ArrayList<String> dumpCursorToStringArrayList(Cursor csr, String tablename) {
ArrayList<String> rv = new ArrayList<>();
int original_position = csr.getPosition();
csr.moveToPosition(-1);
rv.add("Table: " + tablename);
while (csr.moveToNext()) {
rv.add("\tRow # " + String.valueOf(csr.getPosition() + 1));
for (String column: csr.getColumnNames()) {
rv.add("\t\tColumn: " + column + "\tvalue is: \t" + csr.getString(csr.getColumnIndex(column)));
}
}
csr.moveToPosition(original_position);
return rv;
}
I get the same error if I try "" instead of null e.g.
If I use only the rawQuery I get
04-22 07:07:33.914 6271-6271/s.q001 I/RESULTS: Table: RAWQUERY
04-22 07:07:33.914 6271-6271/s.q001 I/RESULTS: Row # 1
04-22 07:07:33.914 6271-6271/s.q001 I/RESULTS: Column: child_mismatches value is: 1
04-22 07:07:33.914 6271-6271/s.q001 I/RESULTS: Column: friend_mismatches value is: 1
This is from using
ArrayList<String> results = DBOpenHelper.checkLinkIntegrity();
for (String s : results) {
Log.i("RESULTS",s);
}
How can I run the query with the query method instead of the rawQuery method, to get the better marks?
Your issue is that the query method expects a table as it then generates SQL as per
SELECT your_columns FROM the_table;
As there is no table it issues the Invalid Table exception.
You have to provide something that will satisfy the FROM clause, it cannot be nothing.
You can get around this in a few ways, which I guess is what the homework is trying to get you to ascertain/explore.
Fix 1
You could supply one of the tables that exist e.g. use
csr = db.query(null,new String[]{sq_child_mismatches,sq_friend_mismatches},null,null,null,null,null,"1");
Note the 8th parameter, this LIMITs the number of rows generated to one as there would be a row generated for each row in the fake table.
Fix 2
or as FROM can be a subquery (see diagram) you could use a subquery e.g. one that you have
So you could use :-
csr = db.query(
sq_child_mismatches, //<<<<<<<<<< the fake subquery
new String[]{
sq_child_mismatches,
sq_friend_mismatches
},
null ,null,null,null,null
);
In this case, as the fake subquery returns a single row/result, there is no need for the LIMIT clause.
I have 3 tables:
Table.Keys, Table.Tags, Table.Values
Table.Keys create table statement:
createTableStatement = "CREATE TABLE " + Tables.KEYS + "("
+ KeysColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ KeysColumns.KEY + " TEXT NOT NULL,"
+ "UNIQUE ("
+ KeysColumns.KEY
+ ") ON CONFLICT IGNORE"
+ ");";
execSQL(sqLiteDatabase, createTableStatement);
Table.Tags create table statement:
createTableStatement = "CREATE TABLE " + Tables.TAGS + " ("
+ TagsColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ TagsColumns.NAME + " TEXT NOT NULL,"
+ "UNIQUE ("
+ TagsColumns.NAME
+ ") ON CONFLICT IGNORE"
+ ");";
execSQL(sqLiteDatabase, createTableStatement);
Table.Value create table statement:
createTableStatement = "CREATE TABLE " + Tables.VALUES + " ("
+ ValuesColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ ValuesColumns.KEY_ID + " TEXT NOT NULL,"
+ ValuesColumns.TAG_ID + " TEXT NOT NULL,"
+ ValuesColumns.VALUE + " TEXT NOT NULL,"
+ "UNIQUE ("
+ ValuesColumns.KEY_ID + ", " + ValuesColumns.TAG_ID
+ ") ON CONFLICT REPLACE"
+ ");";
execSQL(sqLiteDatabase, createTableStatement);
If I do the following join:
Tables.KEYS
+ " JOIN " + Tables.VALUES
+ " ON " + Values.KEY_ID + " = " + Keys.column(Keys._ID)
+ " JOIN " + Tables.TAGS
+ " ON " + Values.TAG_ID + " = " + Tags.column(Tags._ID);
I get duplicate rows of course because the result is
KEY | TAG | VALUE
=================
| |
What I would like to accomplish is to query and get a Cursor from table or view with no duplicate rows with the following schema:
KEY | TAG 1 | TAG 2 | ... | TAG n
=================================
| | | |
Not all keys MUST have values for each tag, but all keys CAN have values.
I'm not sure how to accomplish this. I'm not even sure where to start.
In the meantime I have created another table which stores some TAG values that I know will always exist.
But I feel this is inefficient because at any time I could have 'n' number of new TAG values which is why I would like to be able to create a view or table with the schema I listed.
SQLite has no pivot functions; you have to do this in two steps.
First, get all possible tags:
SELECT _id, Name
FROM Tags
ORDER BY Name;
Then, using the returned data, construct a query that looks up each possible tag for each key:
SELECT Key,
(SELECT Value
FROM Values
WHERE Key_ID = Keys._id
AND Tag_ID = 111
) AS Tag_111,
(SELECT Value
FROM Values
WHERE Key_ID = Keys._id
AND Tag_ID = 222
) AS Tag_222,
...
FROM Keys;
Ok so I have an application that connects to a database that has a customer table. In the customer table I capture a range of different values. VH_ID is a foreign key to the vehicle table and the insurance_ID is also a foreign key to the insurance table.
Any ideas would be appreciated.
Edit
public CustomerInformation getCustomerInfo(String customerName) {
CustomerInformation info = new CustomerInformation();
ResultSet result;
try {
String sqlStatement = "SELECT "
+ DBStrings.C_NAME + ","
+ DBStrings.C_ADDRESS + ","
+ DBStrings.C_PHONENO + ","
+ DBStrings.C_EMAIL + ","
+ DBStrings.C_VH_ID + ","
+ DBStrings.C_VH_MODEL + ","
+ DBStrings.C_VH_YEAR + ","
+ DBStrings.C_VH_REGO + ","
+ DBStrings.C_VH_CHASSIS + ","
+ DBStrings.C_VH_VIN + ","
+ DBStrings.C_INSURANCE
+ " FROM " + DBStrings.CUSTOMER + " WHERE " + DBStrings.C_ID + " = " + this.getCustomerId(customerName);
result = statement.executeQuery(sqlStatement);
while (result != null && result.next()) {
info.setName(result.getString(DBStrings.C_NAME));
info.setAddress(result.getString(DBStrings.C_ADDRESS));
info.setPhoneNumber(result.getString(DBStrings.C_PHONENO));
info.setEmail(result.getString(DBStrings.C_EMAIL));
info.setRego(result.getString(DBStrings.C_VH_REGO) + "");
info.setChassis(result.getString(DBStrings.C_VH_CHASSIS) + "");
info.setVin(result.getString(DBStrings.C_VH_VIN) + "");
info.setVehicleModel(result.getString(DBStrings.C_VH_MODEL) + "");
info.setYear(result.getInt(DBStrings.C_VH_YEAR) + "");
info.setInsurance(this.getInsuranceFromId(result.getInt(DBStrings.C_INSURANCE)));
info.setVehicleMake(this.getVehicleFromId(result.getInt(DBStrings.C_VH_ID)));
}
} catch (SQLException ex) {
Logger.getLogger(Database.class
.getName()).log(Level.SEVERE, null, ex);
}
return info;
}
That is the new code with the ResultSet nested inside the method. Now I am getting the error:
SEVERE: null
java.sql.SQLException: ResultSet not open. Operation 'getInt' not permitted. Verify that autocommit is OFF.
at org.apache.derby.client.am.SQLExceptionFactory40.getSQLException(Unknown Source)
at org.apache.derby.client.am.SqlException.getSQLException(Unknown Source)
at org.apache.derby.client.am.ResultSet.getInt(Unknown Source)
at Database.Database.getCustomerInfo(Database.java:599)
Statement
connection = DriverManager.getConnection(connectionURL, "user", "pass");
statement = connection.createStatement();
Maybe you have column VH_MODEL twice in your query due to a "copy-paste accident" in the constants, i.e. another constant also expands to "VH_MODEL", specifically one of the columns following VH_MODEL in the get sequence. The DBMS would rename the result columns to make them unique in this case I guess. This would explain the strange situation where excuteQuery works while the getString fails.
Essentially the work around that I did to solve this issue was to create another method just specifically for retrieving the vehicle make. Seems a bit pointless but for the life of me I have no idea why it is not accepting it. The method has exactly the same contents for retrieving the data as in the first method.. Seems very odd.
String sqlStatement = "SELECT "
+ DBStrings.C_VH_ID
+ " FROM " + DBStrings.CUSTOMER + " WHERE " + DBStrings.C_ID + " = " + customerId;
rs = statement.executeQuery(sqlStatement);
if (rs != null && rs.next()) {
int vh_id = rs.getInt(DBStrings.C_VH_ID);
vehicle = this.getVehicleFromId(vh_id);
}
I recently updated my database to FTS3 due to that I implented search functionality.
My FTS3 table:
db.execSQL("CREATE VIRTUAL TABLE " + TABLE_FTS + " USING fts3(" + COL_ID + ", " + COL_KEY_NAME + ", "
+ COL_KEY_WEBURL + " , " + COL_KEY_MAINURL + ", " + COL_KEY_CODEC + " " + ");");
I always deletet entrys from my listview with the ContextMenu. I used a method like this:
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
delete(info.id);
updateList();
public void delete(long id) {
int numDeleted = database.delete("stations" , "_ID = ?", new String[] { Long.toString(id) } );
Log.d(TAG, "delete(): id=" + id + " -> " + numDeleted);
}
public void updateList() {
data.requery();
dataSource.notifyDataSetChanged();
}
The method, info.id don't get me the position, so I'am changed it to info.position. Position is right, but delet dont work. Ok, than I tried a simple delete instead of via Android Method.
database.execSQL("DELETE FROM " + DatabaseHelper.TABLE_FTS + " WHERE " + DatabaseHelper.COL_ID + "='" + id + "'");
Don't work, too. It seems that the column, COL_KEY_ID = BaseColumns._ID with an autoincrementing number don't exists in FTS3 anymore? I figured out, that I could use rowid but its not working as intended. If I delete my first entry, the entrys below get weird deleted.
How I can get this back working like before?
Edit: Found now fix ..
Found fix. Don't delete by ID, delete now by name.
I assume the problem comes from
_ID = ?
_ID is an integer yet ? is replaced by a String. Same for your second not working approach.