SQLITE, Group by range of 1000s - android

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.

Related

sqlite get distinct from column 1 where column 1 and column 2 meets multiple requirements

I'm not entirely sure how possible this is in a select statement, or if I'm better getting all results and doing checks myself in Android Studio.
I've got 3 tables, a table that stores Recordings, a Table that stores Tags and a table that links the Tags to the Recordings - TagsLink.
The TagsLink table has 2 columns, one that stores the TagsID and one that stores the RecordingsID
What I'm hoping to do is only return RecordingsIDs that meet the selected Tags criteria. So if TagsID 3 is selected, Recordings 1, 2 and 4 are returned. And if TagsID 3 and 4 are selected, it returns only Recordings 2 and 4.
In my mind it's something along the lines of:
SELECT DISTINCT RecordingsID FROM TagsLink WHERE ...
If this isn't entirely possible, any advice on other ways of achieving this (even if it requires restructuring the database) would be greatly appreciated!
With this kind of query:
SELECT
RecordingsID
FROM
TagsLink
WHERE
TagsID IN (3, 4, ...)
GROUP BY
RecordingsID
HAVING COUNT(*) = 2 -- This number must match the number of tag IDs specified in the IN (...) list.
The key is to remember to adjust the count based on the tags you want to filter on.
Other similar and helpful answers here and here and here.
EDIT
To accommodate additional tables, filtering on different columns, use INTERSECT as follows:
SELECT
RecordingsID
FROM
TagsLink
WHERE
TagsID IN (3, 4, ...)
GROUP BY
RecordingsID
HAVING COUNT(*) = 2 -- This number must match the number of tag IDs specified in the IN (...) list.
INTERSECT
SELECT
RecordingsID
FROM
ContactsLink
WHERE
ContactsID IN (100, ...)
GROUP BY
RecordingsID
HAVING COUNT(*) = 1 -- This number must match the number of contacts IDs specified in the IN (...) list.
This should work:
SELECT RecordingsID FROM tagslink WHERE TagsID = 4
Intersect
SELECT RecordingsID FROM tagslink WHERE TagsID = 3
I could not test it with sqlite. However, the function to use is Intersect, not using parentheses that sqlite does not support them

How to use sql statement Between and like together

I have an sqlite database in android app and I want to search in (zan) table between 1-100 range in (id) column then withing that range find words similar in (eng) column, but it does not return anything and it does not give error either, thanks.
"Select * from zan where id (between 1 and 100) and eng like '"+smilarkeyword+"&'"
Should be id between and the wildchar for like is % (or _)
"Select * from zan
where id between 1 and 100
and eng like '%"+smilarkeyword+"%'"
where % stands for 0 or more characters and _ stands for exactly one character

Does SQLite guarantee the order of results to match a table?

If table Scores looks like this:
_id | score
---------
1 | 1,000
2 | 2,000
3 | 3,000
4 | 4,000
5 | -1
6 | -1
7 | -1
Will the following query always return the rows in _id ascending order?
SELECT * FROM Scores
Also, will the following query always return the first ordered occurrence of _id (that is, 5)?
SELECT _id FROM Scores WHERE Score = -1 LIMIT 0, 1
The key here is ALWAYS. I have tested it and it works as intended but I want to verify this outcome is guaranteed and that an order by clause is not needed in these cases.
Thank you all.
By default, the rows are logically stored in order of increasing rowid.
And if your select stament does not include an order by, or the table has no index, the default sorting order will always be the primary key column, which is the _ID column in this case
Read more here
One of the principles of the databases is that you can not suppose that your registers are ordered in any way, as the internal implementation of SQLite may differ between devices. You should use the operator order by to assure a certain order.

How to run DISTINCT on non-key in SQL

I have a database that can have similar rows, but have different keys and a different boolean column. Here is what the database looks like:
columns: _id, name, img, address, available
Two entries can look like this:
_id | name | img | address | available
-------------------------------------------------------
1 | John | http://img.com/222 | 1 Main St | 1
2 | John | http://img.com/222 | 1 Main St | 0
I want a query that will give me all results that have a distinct key, and if there are duplicate entries(ignoring the fact that _id would be different), it will give back only the first one. Here is the query I have:
SELECT p1.*
FROM (SELECT DISTINCT _id, available FROM people) p
INNER JOIN people p1
ON p1._id=p._id
ORDER BY p1.available DESC;
I know this isn't right, but maybe it explains a little what I am looking for. Would I want to use GROUP BY here?
I want a query that will give me all results that have a distinct key, and if there are duplicate entries(ignoring the fact that _id would be different), it will give back only the first one.....the _id isn't what I want to be distinct, as they [the ids] are already unique. ... . Ideally it will order by 'available' in descending order so that if there are two columns with the same data(aside from _id and available), it will return the row with '1' for the available column
select name, image, address, max(availability) as avail
from T
group by name, image, address
Then you can join the set returned by the query above, as an inline view, to your table:
select * from T
inner join
(
select name, image, address, max(availability) avail
from T
group by name, image, address
) as foo
on T.name = foo.name and T.image = foo.image and T.address = foo.address and T.availability = foo.avail
It would help to have a composite index so: (name, image, address).
Caveat: if there is more than one row where a specific {name, image, address} triad has availablility =1, the query will return multiple rows for the triad:
2 | John | http://img.com/222 | 1 Main St | 1
6 | John | http://img.com/222 | 1 Main St | 1
P.S. It sounds as though you wished the triad (name, image, address) had been created in your table an alternate UNIQUE key.
this sql may solve your problem:
select b.* from (select distinct _id from people) a, people b where a._id = b._id order by b.available
I actually just asked a similar question and received a great answer from an experienced user here:
SQL Populating with distinct data and a sequence
Based on what he told me, perhaps this query would provide you with what you want:
SELECT p1.*
FROM (SELECT DISTINCT _id, name from people) p
INNER JOIN people p1
ON p1._id=p._id
ORDER BY p1.available desc
apologies if that's a fail and doesn't work!
EDIT: It just occurred to me that I have no idea which distinct name+_id combo this will extract.. the available=1 or the available=0 or a random selection..! Let me know what happens anyway..
If you want the first row which has the lowest _id among those that have the highest available value (between 1 and 0), you can "record" the _id inside the aggregated value generated by the grouping.
The value to compare is constructed in a way that orders the record by their available field in descending order and then by their _id field in descending order, and allow to easily retrieve the value of the _id with the modulo operator (assuming available max value is 1 and the ids are never above 100000000).
select people.* from people
inner join (
select name, img, address,
min((1-available)*100000000 + _id) avail_id
from people group by name, img, address
) as foo on people._id = foo.avail_id % 100000000;
I adapted it Tim's query.
You can also do that without subquery:
select people.* from people
left outer join people as other on
other.name = people.name and
other.img = people.img and
people.address=other.address and
(1 - people.available) * 100000000 + people._id >
(1 - other.available) * 100000000 + other._id
where other.available is null;

SQLite on Android: Union with limits alternative

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...)

Categories

Resources