I want to create a Cursor, which later I will pass via CursorAdapter to AutoCompleteTextView for autocompletion suggestions. The problem is that my SQL statement needs to select 2 different result sets from the same table, just sorted by different criteria. At the same time these result sets must not overlap - otherwise I would have multiple same suggestions. On MySQL, this would be quite easy:
(SELECT field3 FROM table ORDER BY field1 ASC LIMIT 5)
UNION
(SELECT field3 FROM table ORDER BY field2 DESC LIMIT 10)
LIMIT 10
In this case I would have always up to 10 unique values. On SQLite, unfortunately, this is not the case - I cannot have limits on particular selects, just on the whole result set, which makes then UNION not useful for this case at all...
To solve this problem I've thought about making 2 queries separately: iterating through Cursor from 1st query and putting values into an Array, then iterating over Cursor from 2nd query and also putting values into the same array while checking and skipping already existing values in Array to avoid the duplicates. Then use ArrayAdapter to pass values to AutoCompleteTextView.
Theoretically this would work - but what I don't like that everything becomes more complex and much more code to write to solve such a simple task...
Is there maybe a better and easier way to do it?
Have you considered doing an artificial intelligence limit, to select from the middle of the recordset. Let me present the idea in an example
First you want to select from the first query the top 5 records. Make the order inversed so it shows up in the end of the recordset. No limit applied here.
SELECT field3 FROM table ORDER BY field1 DESC
Example:
field1 | field2
12 x
11 x
9 x
8 x
7 x
6 x
5 x
4 x
3 x
2 x
1 x
Then do the second query in the order you want. No limit applied here.
SELECT field3 FROM table ORDER BY field2 DESC
Then do an union on them
(SELECT field3 FROM table ORDER BY field1 DESC)
UNION
(SELECT field3 FROM table ORDER BY field2 DESC)
The results you want are in the middle of the resultset
field1 | field2
11 x
10 x
9 x
8 x
7 x
6 x
----------
5 x
4 x
3 x
2 x
1 x
x 1
x 2
x 3
x 4
x 5
x 6
x 7
x 8
x 9
x 10
----------
x 11
Now you can apply an offset and limit to your query. In order to this work, you need to query prior the number of records in your table that are eligible for the query to work.
The final query would be something like:
(SELECT field3 FROM table ORDER BY field1 DESC)
UNION
(SELECT field3 FROM table ORDER BY field2 DESC)
LIMIT 5+10 OFFSET N-5
I wrote the LIMITS explicitly to be able to calculate it yourself, offset N-5 means to skip N-5 records from the beginning of the recordset.
The following will give you the same results as your MySQL example:
CREATE TABLE IF NOT EXISTS unionTemp ( field3 TEXT)
DELETE FROM unionTemp
INSERT INTO unionTemp SELECT field3 FROM table ORDER BY field1 ASC LIMIT 5
INSERT INTO unionTemp SELECT field3 FROM table ORDER BY field2 ASC LIMIT 10
SELECT field3 FROM unionTemp GROUP BY field3 LIMIT 10
You may want to use a temporary table. (CREATE TEMP TABLE...)
Related
Android SQLite version is 3.19 and doesn't support windows function like over and row_number(). I don't have any auto incremented column. I have created view using data from different table. My view details are below.
1 means in stock
2 means out stock
3 means reset count and start from given quantity.
uniquekey is unique id for each row
I want to get running total as below:
uniquekey date ProductName uniqueKeyProduct InOutType quantity runningTotal
edfrgdctydkkc 2020-06-07 Apple dheykdhr 1 10 10
edfrgdctkduxc 2020-06-08 Orange xdefrttk 1 20 20
fdfrgdctydysc 2020-06-08 Apple dheykdhr 2 5 5
3dfrgrtkvctyf 2020-06-08 Apple dheykdhr 1 2 7
ctgrteerylkdc 2020-06-09 Orange xdefrttk 2 8 12
edffjritydmnc 2020-06-10 Orange xdefrttk 3 5 5
kkdjdjrgdctyk 2020-06-10 Apple dheykdhr 3 2 2
egdhgdctyjjdc 2020-06-11 Orange xdefrttk 1 20 25
edfryrytymnbc 2020-06-15 Apple dheykdhr 1 10 12
fgeegdctydk3c 2020-06-18 Apple dheykdhr 2 2 10
hyidfhhhfd89c 2020-06-20 Orange xdefrttk 2 8 17
I don't have any auto incremented column
Assuming that you haven't defined the table using WITHOUT ROWID then you still have an equivalent column, namely the rowid column.
AUTOINCREMENT is an alias of the rowid column, it is inefficient and rarely required
You may wish to read https://www.sqlite.org/rowidtable.html
I want to get running total as below:
As the date column does not determine the insert order (e.g. 2020-06-08 2 Apple rows were processed) the rowid column has been utilised (not 100% failsafe in this regard).
You may wish to consider making the date a column that holds the date and time of the insert.
One way to accomplish the above would be to utilise a TRIGGER. Triggers are driven by an INSERT UPDATE or DELETE event. So it makes sense to use a trigger whenever a row is inserted to then set the runningtotal.
The following TRIGGER maintains the running total for the data that you have provided.
CREATE TRIGGER IF NOT EXISTS mytable_after_insert
AFTER INSERT ON mytable
BEGIN
UPDATE mytable SET runningtotal =
CASE
/* in stock */
WHEN new.inouttype = 1 THEN
COALESCE(
(
SELECT mt1.runningtotal
FROM mytable AS mt1
WHERE mt1.rowid < new.rowid AND mt1.uniquekeyproduct = new.uniquekeyproduct
ORDER BY rowid DESC
LIMIT 1
),0
)
+ new.quantity
/* out stock */
WHEN new.inouttype = 2 THEN
COALESCE(
(
SELECT mt2.runningtotal
FROM mytable AS mt2
WHERE mt2.rowid < new.rowid AND mt2.uniquekeyproduct = new.uniquekeyproduct
ORDER BY rowid DESC
LIMIT 1
),0
)
- new.quantity
/* reset */
WHEN new.inouttype = 3 THEN new.quantity
END
WHERE rowid = new.rowid
;
END
;
note that the table name mytable will likely have to be changed. Perhaps the trigger's name should be changed as well.
The following is the SQL used to test the above. It
Drops the Trigger
Drops the Table
Creates the table (this may need to be amended appropriately)
Creates the Trigger
Inserts all the rows (note that the runningtotal column defaults to 0)
Queries the resultant table showing the runningtotal and also the rowid
:-
DROP TRIGGER IF EXISTS mytable_after_insert;
DROP TABLE IF EXISTS mytable;
CREATE TABLE IF NOT EXISTS mytable
(
uniquekey TEXT PRIMARY KEY,
date TEXT,
productname TEXT,
uniquekeyproduct TEXT,
inouttype INTEGER,
quantity INTEGER,
runningtotal INTEGER DEFAULT 0
)
;
CREATE TRIGGER IF NOT EXISTS mytable_after_insert
AFTER INSERT ON mytable
BEGIN
UPDATE mytable SET runningtotal =
CASE
/* in stock */
WHEN new.inouttype = 1 THEN
COALESCE(
(
SELECT mt1.runningtotal
FROM mytable AS mt1
WHERE mt1.rowid < new.rowid AND mt1.uniquekeyproduct = new.uniquekeyproduct
ORDER BY rowid DESC
LIMIT 1
),0
)
+ new.quantity
/* out stock */
WHEN new.inouttype = 2 THEN
COALESCE(
(
SELECT mt2.runningtotal
FROM mytable AS mt2
WHERE mt2.rowid < new.rowid AND mt2.uniquekeyproduct = new.uniquekeyproduct
ORDER BY rowid DESC
LIMIT 1
),0
)
- new.quantity
/* reset */
WHEN new.inouttype = 3 THEN new.quantity
END
WHERE rowid = new.rowid
;
END
;
INSERT INTO mytable (
uniquekey,date,productname,uniquekeyproduct,inouttype,quantity /* running total not supplied so defaults to 0 */
)
VALUES
('edfrgdctydkkc','2020-06-07','Apple','dheykdhr',1,10),
('edfrgdctkduxc','2020-06-08','Orange','xdefrttk',1,20),
('fdfrgdctydysc','2020-06-08','Apple','dheykdhr',2,5),
('3dfrgrtkvctyf','2020-06-08','Apple','dheykdhr',1,2),
('ctgrteerylkdc','2020-06-09','Orange','xdefrttk',2,8),
('edffjritydmnc','2020-06-10','Orange','xdefrttk',3,5),
('kkdjdjrgdctyk','2020-06-10','Apple','dheykdhr',3,2),
('egdhgdctyjjdc','2020-06-11','Orange','xdefrttk',1,20),
('edfryrytymnbc','2020-06-15','Apple','dheykdhr',1,10),
('fgeegdctydk3c','2020-06-18','Apple','dheykdhr',2,2),
('hyidfhhhfd89c','2020-06-20','Orange','xdefrttk',2,8)
;
SELECT rowid,* FROM mytable ORDER BY rowid ASC
The result being :-
Note that if normalisation rules were followed then there should be
another table for the products and thus that the product name would
not be duplicated, all that would be needed was the uniquekeyproduct.
e.g. if a product name were changed then all rows in the mytable table would have to be updated whereas if there were a product table a name changed would be just need the 1 row to be updated and it would be reflected when querying mytable (the ProductName column would no longer be required).
COALESCE is used to convert the null returned when the first row for a product is inserted as there are no rows that match the selection criteria for in stock and out stock actions. see https://www.sqlitetutorial.net/sqlite-functions/sqlite-coalesce/
CASE ..... END is for conditional processing of the inouttype column. It has 3 WHEN clauses one for each of the types (1,2 or 3). see https://www.sqlite.org/lang_expr.html#case
In the subqueries used within the first two WHEN clauses tables are given a temporary name mt1 and mt2 respectively, using AS to ensure that there is no ambiguity as to where the data is to come from.
Note that the as 3.19 is quite old the above was tested on more recent versions so there may be some issues. You may wish to have a look through https://www.sqlite.org/changes.html if you come across issues.
I have ids in my table, ids start from 1 to 20, I want a query, to find the first and last records in a given table but I want the result by some condition.
For example: if I have the record
1,2,3,4,5,9,10,11,12,13, 19,20
I need a result like 1-5, 9-13, 19-20 like this I need results
This is the island part of the classic gaps and islands problem (With the gaps part being finding the missing values in between each island). If you search for that term, you'll find a ton of material about how to calculate them.
One approach (Requires Sqlite 3.25 or newer for window function support):
sqlite> CREATE TABLE ex(id INTEGER PRIMARY KEY);
sqlite> INSERT INTO ex VALUES (1),(2),(3),(4),(5),(9),(10),(11),(12),(13),(19),(20);
sqlite> WITH cte AS (SELECT id, id - row_number() OVER (ORDER BY id) AS grp FROM ex)
...> SELECT min(id) AS rangestart, max(id) AS rangeend FROM cte GROUP BY grp;
rangestart rangeend
---------- ----------
1 5
9 13
19 20
SQL Query to find first record in your table:
SELECT * FROM <table_name> ORDER BY <column_name> ASC LIMIT 1
SQL Query to find last record in your table:
SELECT * FROM <table_name> ORDER BY <column_name> DESC LIMIT 1
For example: if I have the record 1,2,3,4,5,9,10,11,12,13, 19,20
I need a result like 1-5, 9-13, 19-20 like this I need results
If you need result like you have mentioned, then you can set LIMIT in your query to get how many records you can have in that query.
QUERY:
SELECT * FROM <table_name> LIMIT <any_number>
I have a database of two columns in sqlite for android
id (INT) | owner (VARCHAR)
1477 jack
1578 jill
: :
9277 hill
1)
i like to get a count of the following: -
(group by the 1000s range)
RANGE | COUNT
0-999 0
1000-1999 5
:
8999-9999 7
2)
I also like to get a count of the following: -
(group by 100s range)
RANGE | COUNT
X1XX 5
X2XX 6
:
X9XX 7
3) and also group by 10s range.
I'm stuck with how to do the group by
SELECT COUNT(*) FROM myTable
GROUP BY ??
any pointers is appreciated.
Integer division by integers in sqlite3 yields integers:
select id/1000 as range_id, count(*) as range_count from mytable group by range_id;
To group by 100 ids, simply change 1000 to 100
sqlite3 has a printf() function that might be useful in make a pretty column to describe the range if you need something prettier than the integer division of id by group size.
I have two identical tables (month1, month2) and I am trying to find all records from both tables where task1_done = 1. I want the last row in that set (i move cursor to last for this). I have played with inner outer natural joins but can't seem to get month2 values. Here is what I have:
String query = "SELECT m1.columnA, m1.columnB, m1.columnC, m1.columnD, m1.columnE, m1.columnF FROM month1 m1, month2 m2 WHERE m1.task1_done = 1 OR m2.task1_done = 1";
Any help would be great!
I think you want a union all for this query:
select m.*
from (select *
from months1
union all
select *
from months2
) m
where task1_done = 1;
Note: I have used * as a convenience because you said the two tables have the same structure. You should actually list the columns that you want from the two tables.
In general, having two tables with the same layout is a sign of a bad database design. It is usually better to have a bigger table, with another column identifying "month1" or "month2".
EDIT:
SQL tables do not have a "last" value. If you have a an id or timestamp column that you can use for ordering, then you can do:
select m.*
from (select *
from months1
union all
select *
from months2
) m
where task1_done = 1
order by id desc
limit 1;
Are these tables related or have any references? if not you can have separate statement and do a union
i.e.
select top 1 column1, column2.. from month1 WHERE task1_done = 1 order by IdentityColumn Desc
union
select top 1 column1, column2.. from month2 WHERE task1_done = 1 order by IdentityColumn Desc
I know can select the next or previous X records with statements like the following:
Next: `SELECT * FROM table WHERE some_key > 3 ORDER BY some_key ASC LIMIT X`
Previous: `SELECT * FROM table WHERE some_key < 3 ORDER BY some_key DESC LIMIT X`
However I'm working in a language which returns a Cursor over the results and I'd like all of my results in one cursor.
Is it possible to select the current record, along with X next and previous records in a single SQL statement?
You could try using a UNION:
SELECT * FROM table WHERE some_key > 3 ORDER BY some_key ASC LIMIT X
UNION
SELECT * FROM table WHERE some_key = 3
UNION
SELECT * FROM table WHERE some_key < 3 ORDER BY some_key DESC LIMIT X
You might need to fix up the ordering outside the unions but the above will give you the window around some_key = 3 that you're looking for and it will cleanly deal with gaps in the some_key values.
In the end I used the answer from #mu, with some modifications
In Android, you cannot use inner ORDER BY statements in a UNION, just a single outer ORDER BY on the whole set. To get around this I used the fact that SQLite on Android does support inner queries, performed each actual select in an inner query and then selected all rows from the inner query to union, as follows:
SELECT * FROM (SELECT * FROM table WHERE some_key > 3 ORDER BY some_key ASC LIMIT X)
UNION
SELECT * FROM table WHERE some_key = 3
UNION
SELECT * FROM (SELECT * FROM table WHERE some_key < 3 ORDER BY some_key DESC LIMIT X)