I have problem with SQL in Android (SQLite).
I have three tables in relation many-to-many, suppose A, B and C, each has 30 records. I want to join these tables and after that use WHERE clause. I have written something like this code
SELECT *
FROM A
JOIN B ON A.id=B.photoid
JOIN C ON B.urlid=C.id
WHERE C.type=[SOME VALUE]
I have also tried this
SELECT *
FROM A
LEFT OUTER JOIN B ON A.id=B.photoid
LEFT OUTER JOIN C ON B.urlid=C.id AND C.type=[SOME VALUE]
and this
SELECT *
FROM A
CROSS JOIN B
CROSS JOIN C
WHERE A.id=B.photoid AND B.urlid=C.id AND C.type=[SOME VALUE]
These statements don't work in Android. They return 300 records.
What should I change? Is it bug or I do something wrong?
Hard to tell without any additional data, but keep in mind that a JOIN operation will produce a row for each combination of a value in any of the tables involved. If you had 3 tables, each with 30 rows and you wouldn't be filtering any result, it should produce 27000 rows, but you're filtering by A.id=B.photoid and B.urlid=C.id, which might be producing a smaller set of rows.
In my opinion, that's not anything related to SQLite, but to your query instead. I'd try to make your set of values much smaller (for example, 5 per table) and run the same query, and see which results are being returned which shouldn't be, and try to modify your query to make it discard all those unwanted results.
Related
I need get the total SUM for each rows in my query, but I don't want go twice in the table.
I tried do this:
SELECT id, value, SUM(value) as total FROM product
But my result was this:
id value total
3 30 60
If I do the bellow query I get my wanted result, but I need go twice in the table:
SELECT id, value, (SELECT SUM(value) FROM product) as total FROM product
Or if I use 'WITH' clause, but this is not supported before Android 5:
WITH data AS (SELECT id, value FROM product)
SELECT id, value, (SELECT SUM(value) FROM data) as total FROM data
Wanted result:
id value total
1 10 60
2 20 60
3 30 60
Thank you!
It's not possible using your SQLite version. You'll have to use two selects.
Basically you have to use a subquery.
However, perhaps you may be less concerned about the 2nd table as I believe that the Query Planner will determine that it only needs to calculate the sum once and does away with the need for a variable as it stores the value in cache.
I believe that the results of using EXPLAIN QUERY PLAN your_query shows this. i.e. using
EXPLAIN QUERY PLAN SELECT id, value, (SELECT sum(value) FROM products) AS total FROM products;
results in :-
This being explained as (see bolded statements) :-
1.3. Subqueries
In all the examples above, the first column (column "selectid") is
always set to 0. If a query contains sub-selects, either as part of
the FROM clause or as part of SQL expressions, then the output of
EXPLAIN QUERY PLAN also includes a report for each sub-select. Each
sub-select is assigned a distinct, non-zero "selectid" value. The
top-level SELECT statement is always assigned the selectid value 0.
For example:
sqlite> EXPLAIN QUERY PLAN SELECT (SELECT b FROM t1 WHERE a=0), (SELECT a FROM t1 WHERE b=t2.c) FROM t2;
0|0|0|SCAN TABLE t2
0|0|0|EXECUTE SCALAR SUBQUERY 1
1|0|0|SEARCH TABLE t1 USING COVERING INDEX i2 (a=?)
0|0|0|EXECUTE CORRELATED SCALAR SUBQUERY 2
2|0|0|SEARCH TABLE t1 USING INDEX i3 (b=?)
The example above contains a pair of scalar subqueries assigned
selectid values 1 and 2. As well as a SCAN record, there are also 2
"EXECUTE" records associated with the top level subquery (selectid 0),
indicating that subqueries 1 and 2 are executed by the top level query
in a scalar context. The CORRELATED qualifier present in the EXECUTE
record associated with scalar subquery 2 indicates that the query must
be run separately for each row visited by the top level query. Its
absence in the record associated with subquery 1 means that the
subquery is only run once and the result cached. In other words,
subquery 2 may be more performance critical, as it may be run many
times whereas subquery 1 is only ever run once.
Unless the flattening optimization is applied, if a subquery appears
in the FROM clause of a SELECT statement, SQLite executes the subquery
and stores the results in a temporary table. It then uses the contents
of the temporary table in place of the subquery to execute the parent
query. This is shown in the output of EXPLAIN QUERY PLAN by
substituting a "SCAN SUBQUERY" record for the "SCAN TABLE" record that
normally appears for each element in the FROM clause. For example:
sqlite> EXPLAIN QUERY PLAN SELECT count(*) FROM (SELECT max(b) AS x FROM t1 GROUP BY a) GROUP BY x;
1|0|0|SCAN TABLE t1 USING COVERING INDEX i2
0|0|0|SCAN SUBQUERY 1
0|0|0|USE TEMP B-TREE FOR GROUP BY
If the flattening optimization is used on a subquery in the FROM
clause of a SELECT statement, then the output of EXPLAIN QUERY PLAN
reflects this. For example, in the following there is no "SCAN
SUBQUERY" record even though there is a subquery in the FROM clause of
the top level SELECT. Instead, since the flattening optimization does
apply in this case, the EXPLAIN QUERY PLAN report shows that the top
level query is implemented using a nested loop join of tables t1 and
t2.
sqlite> EXPLAIN QUERY PLAN SELECT * FROM (SELECT * FROM t2 WHERE c=1), t1;
0|0|0|SEARCH TABLE t2 USING INDEX i4 (c=?)
0|1|1|SCAN TABLE t1
EXPLAIN QUERY PLAN
End Note
Perhaps of relevance is this statement :-
The best feature of SQL (in all its implementations, not just SQLite)
is that it is a declarative language, not a procedural language. When
programming in SQL you tell the system what you want to compute, not
how to compute it. The task of figuring out the how is delegated to
the query planner subsystem within the SQL database engine.
Query Planning
You may also find this of interest he SQLite Query Optimizer Overview noting that as of release 3.8.0 The Next-Generation Query Planner is utilised.
I'm using this in android but using SQLite Studio 2.1.4 for testing.
I have two tables: A and B.
A has 2 records and B has no records.
If I query
select * from A
I get the results in that table, which is fine.
If I query
select * from B
I get no results, which is also fine.
However, if I query:
select * from A, B
I also get no results, which is wrong... I should get the results from A, right?
Or is SQLite somewhat different from the other DBMS out there?
SELECT * from A, B is a cartesian join of both tables. In other words every row in table A is joined to every row in table B. So if A has 5 rows and B has 10 rows, you end up with 50 rows in your results set. In this case, table B has zero rows hence it doesn't matter how many rows A contains, you get no rows back.
You will find the same behaviour in all relational databases.
This is for Android SQLite. I have two queries like this:
select * from table where name='name';
and
select * from table where name!='name' order by name;
I want to create a statement which combines these two queries. I tried union all but I can't do order by one statement and then combine. I tried this:
select * from table where name='name'
union all
select * from table where name!='name' order by name;
All it did is to combine the queries and then order by name. I don't want that. I want to do order by on the second statement first and then combine them.
To put the question differently, here is my data:
Name
a
b
c
d
e
f
g
h
i
j
But I want the output to be:
Name
g
a
b
c
d
e
f
h
i
j
I want to get one row to the top and then order the rest of the rows. Any help is appreciated.
No need to use temporary tables, you need to add an additional column to sort on. Something like this:
select 1, * from table where name='name'
union all
select 2, * from table where name!='name'
order by 1, name;
I don't have a sqlite install right now, but this trick should work. (you may have to add an alias to the first column).
Unless there is some other attribute of the table you can use to provide sorting that allows a join between the two selects as in How to combine two sql queries? then I think you'll have to store the result of the query that should float to the top in a temporary table and then add the sorted results to that table before storing it.
I've never used temporary tables in Android so can't provide an example but as far as I'm aware it's possible.
I'd recommend running the two queries separately and then combining the results in code if that's possible in your situation.
According to the SQLLite docs this cannot be done with a UNION or UNION ALL because those operations must be performed on a simple select, (ones without Order by).
http://www.sqlite.org/lang_select.html
There's probably a very clever way to do this that I don't know, which generally leads me to just do two queries and combine the results in java.
[EDIT] And Jhovanny has the very clever way to do it.
Can't test it right now, but something like this should work:
select t.*, case when name = 'name' then 0 else 1 as o from table t order by o, name;
Then you don't have the two selects nor the union. Assuming you can use a case statement in sqlite on android.
I was wondering if it's possible (it should be) to query multiple tables simultaneously (several at once) in SQLite. Basically I have several tables that have the exact same columns, but the data in them is just organized by the table it's in. I need to be able to use SELECT to get data from the tables (I heard UNION could help), which matches a condition, then group the data by the table it's in.
In other words, would something like this be possible?
SELECT * FROM table1,table2,table3,table4,table5,table6 WHERE day=15 GROUP BY {table}
I'd rather not resort to having to query the tables individually as then I would have a bunch of Cursors that I'd have to manually go through and that would be difficult when I only have one SimpleCursorAdapter? Unless a SimpleCursorAdapter can have several Cursors?
Thanks.
EDIT: The structure of my tables:
Main Table - contains references to subtables in a column "tbls"
and meta-information about the data stored in the subtables
Subtable - contains reference to subsubtables in a column "tbls"
and meta-information about the data stored in the
subsubtables
Subsubtable - contains the actual entries
Basically these tables just make it easier to organize the hierarchical data structure. I suppose instead of having the subsubtables, I could keep the actual entries in the subtable but add a prefix, and have a separate table for the meta-information. It just seems it would be harder to delete/update the structure if I need to remove a level in this data set.
You can create view based on your tables, the query of your view is union of your tables.
create view test as select * from table1 union select * from table2
now you can filter data as you want
for more info check union & view
http://www.w3schools.com/sql/sql_union.asp
http://www.w3schools.com/sql/sql_view.asp
In the end, I decided to forgo having many subsubtables, and instead adding another column like Tim and Samuel suggested. It will probably be more efficient as well then chaining SELECTs with UNION.
I want to add the results of two separate counting SQlite queries. Suppose I have 2 tables named entries and scores and have 2 queries:
SELECT COUNT(1) FROM entries WHERE
key NOT IN (SELECT key FROM scores)
SELECT COUNT(1) FROM scores WHERE
value <= threshold
Maybe I could do something like this to get the sum of their results:
SELECT COUNT(1) from (
SELECT key FROM entries WHERE
key NOT IN (SELECT key FROM scores)
UNION ALL
SELECT key FROM scores WHERE
value <= threshold
)
But is this a little too inefficient? This is called pretty often and may interfere with the UI's smoothness.
Thank you.
[EDIT] What I'm actually trying to do:
I'm making an app to help learning vocabulary. The entries table keeps 'static' data about word type, definition, etc. The scores table keeps information about how well you've learned the words (e.g. performance, scheduled next review time)
To check for the number of remaining words to learn/review, I count how many words do not exist in the scores table yet (i.e. never touched) or when the accumulated score is pretty low (i.e. needs reviewing).
The reason I don't merge those 2 tables into 1 (which would make my life much easier) is because sometimes I need to update the entries table either by inserting new words, deleting a few words, or updating their content, and I haven't found a simple way to do that. If I simply do INSERT OR REPLACE, I will lose the information about scores.
I think you're looking for a UNION. A union combines the results from two queries. Try this (sorry it isn't tested, I don't have access to SQLite):
SELECT COUNT(1) FROM
(
SELECT 1
FROM entries
WHERE key NOT IN (SELECT key FROM scores)
UNION ALL
SELECT 1
FROM scores
WHERE scores.value <= threshold
)
After reading the edit in your question explaining what you need to do, I think a JOIN would be more appropriate. This is a way of combining two tables into one query. Something like this:
SELECT COUNT(1)
FROM entries
LEFT JOIN score
ON score.key = entries.key
WHERE score.value <= threshold
OR score.key is null