I'm working with a ListView that has a CursorAdapter and the query is done via LoaderManager to my ContentProvider. Everything seems to work fine, but when I try to do a multiple query with the same table I get a duplicated column error:
table rating:
CREATE TABLE rating ( _id INTEGER PRIMARY KEY, message TEXT NOT NULL,
from_user_id INTEGER NOT NULL, to_user_id INTEGER NOT NUL);
table users:
CREATE TABLE users ( _id INTEGER PRIMARY KEY, name TEXT NOT NULL,
avatar TEXT, GENDER TEXT);
QueryBuilder:
queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(rating LEFT JOIN users ON
rating.from_user_id = users._id LEFT JOIN users
ON rating.to_user_id = users._id );
Is there any way to use an alias or any similar approach?
Yes, you can use the as keyword to set an alias for a table. Check out https://www.sqlite.org/syntax/join-clause.html and https://www.sqlite.org/syntax/table-or-subquery.html.
Your join clause would look like this:
rating LEFT JOIN users as from_users ON rating.from_user_id = from_users._id
LEFT JOIN users as to_users ON rating.to_user_id = to_users._id
Actually you only need to set an alias for one of both occurrences of users, but setting an alias for both makes it easier to read and understand.
Note that you also need to prefix all columns that come from the users table in your projection and all other parts of the query (e.g. where, order by).
So given that you're interested in the user names of both users (the rating user and the rated user), your projection would look like this:
new String[]{"from_users.name", "to_users.name"};
Related
I am trying to create a view using the following source code:
SQLiteDatabase db = mManagerDbHelper.getWritableDatabase();
String sql = "SELECT * FROM users WHERE name = ?";
String[] selectionArgs = new String[] {"Bob"};
db.execSQL("CREATE VIEW bob_user AS " + sql, selectionArgs);
However, this code always returns this error:
android.database.sqlite.SQLiteException: parameters are not allowed in views (code 1)
How do I use the bindArgs parameter of execSQL(String, Object[]) method?
How do I use the bindArgs parameter of execSQL(String, Object[]) method?
In short you can't, a VIEW is intended to be stored and thus cannot be dynamically changed and hence why you can't bind parameters.
You could however get around it by using an expression for the right hand expression of the WHERE clause and if that expression were to select the value from a table then the value could be changed in that table (i.e. mimicbind in the following) e.g.
DROP VIEW IF EXISTS a_user;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS mimicbind;
CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO users (name) VALUES('bob'),('mary'),('fred'),('sue');
CREATE TABLE IF NOT EXISTS mimicbind (mimicname TEXT PRIMARY KEY, value TEXT);
INSERT INTO mimicbind VALUES('a_user','bob');
CREATE VIEW a_user AS SELECT * FROM users WHERE name = (SELECT value FROM mimicbind WHERE mimicname = 'a_user');
SELECT * FROM a_user;
UPDATE mimicbind SET value = 'sue' WHERE mimicname = 'a_user';
SELECT * FROM a_user;
DROP VIEW IF EXISTS a_user;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS mimicbind;
The result from the queries being:-
and then after the value column in the mimicbind table is changed from bob to sue then:-
However, you may wish to consider what benefit there is to using a view as it would appear that for what you want it is an unnecessary complexity, when a straight forward select would likely be as, if not more efficient. That is a VIEW will, I believe. take up a minimum of 4k per VIEW so bob, alice, etc .... and that's going to be storage space largely wasted.
Even the solution via another table is probably not going to afford any benefit over just using a query. The end result of both would be a Cursor accessing exactly the same core data *(of course in the solution then mimicbind table is additionally accessed)(
I never found a solution here in stackoverflow. So I modified my sql statement and ended up with the following code:
SQLiteDatabase db = mManagerDbHelper.getWritableDatabase();
String sql = "SELECT * FROM users WHERE name = 'Bob'";
db.execSQL("CREATE VIEW bob_user AS " + sql);
Note that I used execSQL(String).
In my Android app, I create a FULLTEXT table like this:
CREATE VIRTUAL TABLE products USING fts3 (
_id integer PRIMARY KEY,
product_name text NOT NULL,
...
)
And I add this index:
CREATE INDEX product_name_index ON products (product_name)
The app populates the table with various products, each with a unique _id value.
However, when I then try to insert an already-existing product ID (using an _id value that is already in the table, but with a different product_name value) like this:
long rowId = db.insertOrThrow("products", null, contentValues);
a new row is added to the table (with a brand new rowId value returned)!
I expected the insertOrThrow command to fail, so where am I going wrong? Is it something to do with the fact that it's a FULLTEXT table or could the index I specified on the product_name column be messing things up somehow?
I read this section about INTEGER PRIMARY KEY, but unfortunately I'm none the wiser.
Update
When I try to perform the same operation on a standard (non-FULLTEXT) table, then the insertOrThrow command results in the expected SQLiteConstraintException.
I think the issue might be that an FTS table has the concept of a docid and a rowid column and specifying null for the docid results in that being given a value.
as per :-
There is one other subtle difference between "docid" and the normal
SQLite aliases for the rowid column.
Normally, if an INSERT or UPDATE
statement assigns discrete values to two or more aliases of the rowid
column, SQLite writes the rightmost of such values specified in the
INSERT or UPDATE statement to the database.
However, assigning a
non-NULL value to both the "docid" and one or more of the SQLite rowid
aliases when inserting or updating an FTS table is considered an
error. See below for an example.
1.3. Populating FTS Tables
I have a table in a SIP app that stores the call history for all your accounts. I am not a friend of multi-column primary keys, so I put an auto-increment column as my PK.
The first columns of the table are
CREATE TABLE IF NOT EXISTS CALLHISTORY
(
CALLHISTORYID INTEGER PRIMARY KEY AUTOINCREMENT,
ACCOUNTID INTEGER NOT NULL,
CALLID TEXT NOT NULL,
... + many more columns
I get a callId from the CallManager (SIP Server), which is unique for an account (so accountId + callId together build a unique pair).
I set up indices like this:
CREATE INDEX IF NOT EXISTS IX_CALLHISTORY_ACCOUNTID ON CALLHISTORY (ACCOUNTID);
CREATE UNIQUE INDEX UIX_CALLHISTORY_ACCOUNTID_CALLID ON CALLHISTORY (ACCOUNTID,CALLID);
I have several queries on this table in the app, some querying only the accountId, some querying the pair (depends on Activity).
Do I really need both indices or will queries that have only the accountId in the where clause use the unique index too?
Thanks for your help!
From the doc (1.6 Multi-Column Indices):
A multi-column index follows the same pattern as a single-column
index; the indexed columns are added in front of the rowid. The only
difference is that now multiple columns are added. The left-most
column is the primary key used for ordering the rows in the index. The
second column is used to break ties in the left-most column. If there
were a third column, it would be used to break ties for the first two
columns. And so forth for all columns in the index. Because rowid is
guaranteed to be unique, every row of the index will be unique even if
all of the content columns for two rows are the same.
As a result, the index of the ACCOUNTID and CALLID fields already handles the ordering of the ACCOUNTID so creating index on ACCOUNTID is unnecessary in this case.
According to some blogs like http://reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/ and even in some of the aswers here.
One of the first steps before including the datababe into the project is to rename the primary id field of your tables to "_id" so Android will know where to bind the id field of your tables.
What should be done with a table that have a combined primary key
Assume that i'm creating the relation between the product id and the store id to assign it's price.
CREATE TABLE `Products-Stores` (
`product` INTEGER NOT NULL,
`store` INTEGER NOT NULL,
`price` INTEGER NOT NULL,
PRIMARY KEY(product,store)
);
There is no need to rename any column in your database. SQL allows column aliases like this:
SELECT integer_primary_key AS _id
...
The only time this is necessary is when you are using a ListAdapter to display the contents of a cursor queried from your DB. You must have an integer primary key column, named "_id" in the cursor, to do that
Better yet, every SQLite database table has an implicit column named "rowid". You don't even have to have your own integer primary key column. Just use rowid, like so:
SELECT rowid AS _id
...
EDITED TO INCLUDE #CL's EXPLANATION OF WORKING JOINS
Obviously, this trick won't work, for many kinds of joins. As long as the rowids are unique over all the rows in the join, though, it works fine.
i have two tables DISCIPLINE and SUBJECT.
DISCIPLINE table has _DISCIPLINE_ID as a primary key and a DISCIPLINE_Name column.
SUBJECT table has _SUBJECT_ID as a primary key SUBJECT_Name and DISCIPLINE as a Forign key.
i want to select Subject from SUBJECT table Who has the same _DISCIPLINE_ID in the DISCIPLINE table.
here is my query:
SELECT DISCIPLINE._DISCIPLINE_ID,
SUBJECT.SUBJECT_Name
FROM DISCIPLINE,
SUBJECT
WHERE SUBJECT.DISCIPLINE = DISCIPLINE._DISCIPLINE_ID
it gives me data but it selects all the Subjects and DISCIPLINE.
i think it is how your from clause is built, have you tried inner join instead of a comma seperating the table selections?