Show first n rows sorted by one column but they should be unique by another column (SQLite, Android Room) - sqlite

A simple select * from mytable will return below rows. I don't know how to draw table in post so I am adding the image
As I mentioned in the question title:
(i) show first n rows sorted by one column (can be achieved using order by)
(ii) but they should be unique by another column (unique by collectionID column)
select * from mytable
order by lastAccessTime DESC;
this sorts the table in descending order according to their lastAccessTime as shown in below image:
Now I want to filter these rows according to their collectionID. So only 1 row per collectionID. I have added the image. The strikethrough rows should be removed.
Also, First n rows (lets say 30) should be returned.
I am using Android Room ORM which uses SQLite but to get the desired result set I have to write the correct query.

I think you need a window function filter here. Which will assign a row number based on collectionID and then you can just fetch only 1 row per collectionID. You may give a try to -
SELECT *
FROM (SELECT *, ROW_NUMBER() OVER(PARTITION BY collectionID ORDER BY ID DESC) RN
FROM mytable) T
WHERE RN = 1
LIMIT 30;

The key idea is to "filter" the data with one query which is the source of another query. A window function can be used as in the other answer, but a basic sub-query is also sufficient:
SELECT *
FROM mytable
INNER JOIN
(SELECT Max(id) AS singleID, collectionID
FROM mytable
GROUP BY collectionID) AS filter
ON mytable.id = filter.singleID
ORDER BY lastAccessTime DESC
LIMIT 30;

Related

Get LIMIT value from subquery result

I would like to use the LIMIT option in my query, but the number of expected rows is stored in another table. This is what I have, but it doesn't work:
select * from table1 limit (select limitvalue from table2 where id = 1)
When I only run the subquery, the result is 6, as expected.
I prefer working with a WITH statement if possible, but that didn't work eiter.
Thank you in advance!
You could use a prepared statement to get the limit of queries from the other table because the limit clause does not allow non constant variables as parameter:
PREPARE firstQuery FROM "SELECT * FROM table1 LIMIT ?";
SET #limit = (select limitvalue from table2 where id = 1);
EXECUTE firstQuery USING #limit;
The source of the sql query from another post
You can make use of MariaDB's ROW_NUMBER function in a CTE to count the rows to be output, comparing that against the limitvalue. For example:
WITH rownums AS (
SELECT *,
ROW_NUMBER() OVER () AS rn
FROM table1
)
SELECT *
FROM rownums
WHERE rn <= (SELECT limitvalue FROM table2 WHERE id = 1)
Note Using LIMIT without ORDER BY is not guaranteed to give you the same results every time. You should include an ORDER BY clause in the OVER part of the ROW_NUMBER window function. With the sample data in my demo, you might use something like:
ROW_NUMBER() OVER (ORDER BY mark DESC)
Demo on dbfiddle

SQLite left join with two conditions in right table

I have two tables: a list of items and the sort order scoped on some group_id.
Items belong either to a group or are common (group_id=0).
I want to query all common and group items with the correct sort order for this group. All items with sort order -1 must be dismissed. Items with no associated sort_order must be included.
Wrong (Naive) Query:
SELECT items.*
FROM items LEFT JOIN sort_order ON items._id = sort_order.item_id
WHERE (items.group_id=0 OR items.group_id=14)
AND sort_order.entity_id=14
AND sort_order.sort >= 0
Behaves like a inner join - items with no corresponding sort_order are dismissed.
Slow Query:
SELECT items.*
FROM items LEFT JOIN sort_order
ON items._id = sort_order.item_id AND sort_order.entity_id=14
WHERE (items.group_id=0 OR items.group_id=14)
AND sort_order.sort >= 0
With ~5.000 entries in sort_order and ~1500 items the query takes ~2 seconds.
My question: Is there a better/correct way to handle this?
Maybe nonsense:
SELECT items.* FROM items WHERE items.id not in (Select id from sort_order)
UNION
SELECT items.* FROM items INNER JOIN sort_order ON items._id = sort_order.item_id AND sort_order.entity_id=14 WHERE (items.group_id=0 OR items.group_id=14) AND sort_order.sort >= 0
Found the source of the slow query: I forgot to create an index containing both sort_order.item_id and sort_order.item_id.
Adding a combined index did the trick for me:
CREATE INDEX sort_order_item_group
ON sort_order (order_id, group_id);

Sqlite double left outer join with count

I have the following DB structure:
tbl_record(_id,_id_user,...)
tbl_photo(_id,_id_record,...)
tbl_note(_id,_id_record,...)
When listing the records of a specific user while counting the number of photos a record has, I use the following query, which works fine:
SELECT tbl_record._id, COUNT(tbl_photo._id_record) AS photo_count FROM tbl_record
LEFT OUTER JOIN tbl_photo ON tbl_record._id=tbl_photo._id_record
WHERE tbl_record._id_user=? GROUP BY tbl_record._id;
Now, I'd like to do the same as above, but also count the number of notes a record has:
SELECT tbl_record._id, COUNT(tbl_photo._id_record) AS photo_count, COUNT(tbl_note._id_record) AS note_count FROM tbl_record
LEFT OUTER JOIN tbl_photo ON tbl_record._id=tbl_photo._id_record
LEFT OUTER JOIN tbl_note ON tbl_record._id=tbl_note._id_record
WHERE tbl_record._id_user=? GROUP BY tbl_record._id;
The count of the 2nd query does not work properly when a record has >0 photos & >0 notes, e.g. 3 photos & 5 photos which results in a count of 15 (3*5) for each.
Any idea how to make the 2nd query return the proper counts?
Thanks!!
You might be able to filter out duplicates by using COUNT(DISTINCT some_id), but this would be inefficient.
Better use correlated subqueries:
SELECT _id,
(SELECT COUNT(*)
FROM tbl_photo
WHERE _id_record = tbl_record._id
) AS photo_count,
(SELECT COUNT(*)
FROM tbl_note
WHERE _id_record = tbl_record._id
) AS note_count
FROM tbl_record
WHERE _id_user = ?

How to "update" the _id column in SQLite Database Browser

The _id column in my database is an INTEGER PRIMARY KEY, so it is an auto-incrementing column.
The problem is that now I deleted a row, and the column didn't update the auto-incrementing number.
Is there a way to make the _id column update, so there wouldn't be holes in the sequence?
Thank you very much in advance.
No. This is not how it is intended to be used. Don't mess with the primary key! There will be gapes. The id is just a unique identifier.
If you need a rank then you can do that
select t.*, #rank := #rank + 1 as gapless_rank
from your_table t
cross join (select #rank := 0) r
order by id
To get the nth ID from the table, use a query like this:
SELECT _id
FROM MyTable
ORDER BY _id
LIMIT 1
OFFSET n-1

Query to find 'most watched' [COUNT()] from one table while returning the results from another

The question probably is quite confusing.
In affect i have the following:
WatchList table
UserId | FilmId
| 3 77
| etc etc
|
|
|
these are foreign keys for the following tables
FilmDB - Film_title, Film_plot, Film_Id etc.
and
aspnet_memberships - UserId, Username etc..
Now, i presume i will need to use a join but i am struggling with the syntax.
I would like to use 'Count' on the 'WatchList' and return the most frequent filmId's and their counterpart information, but i'd then like to return the REST of the FilmDB results, essentially giving me a list of ALL films, but with those found in the WatchedList my frequently sorted to the top.
Does that make sense? Thanks.
SELECT *
FROM filmdb
LEFT JOIN (
SELECT filmid, count(*) AS cnt
FROM watch_list
GROUP BY filmid) AS a
ON filmdb.film_id = a.filmid
ORDER BY isnull(cnt, 0) DESC;
http://sqlfiddle.com/#!3/46b16/10
You did not specify if the query should be grouped by film_id or user_id. The example I have provided is grouped by user if you change that to film_id then you will get the watch count for all users per film.
You need to use a subquery to get the count and then order the results by the count descending to get an ordered list.
SELECT
*
FROM
(
SELECT
WatchList.Film_Id,
WatchCount=COUNT(*)
FilmDB.Film_Title
FROM
WatchList
INNER JOIN FilmDB ON FilmDB.Film_Id=WatchList.Film_Id
GROUP BY
WatchList.UserID,
WatchList.Film_Id,
FilmDB.Film_Title
)AS X
ORDER BY
WatchCount DESC

Resources