Sqlite double left outer join with count - sqlite

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 = ?

Related

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

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;

Sqlite | How do I select records from two different tables using Case?

Edit
I would rather explicitly state my problem I'm facing rather than assuming the approach. There could be an easiest solution to my problem.
I need to select records by joining two different table based on the result from another table. And I have to use different joins based on the result from the first table.
If a particular record is present in the first table, I have two use inner join the first table whereas if it's not present, then I have to left join.
bool recordPresent = select exists (select * from firstTable where access_id = 13) as access
if (recordPresent)
results = select * from secondTable s left join firstTable f on f.access_id = s.access_id where f.access_id is null order by access_id
else
results = select * from secondTable s inner join firstTable f on f.access_id = s.access_id

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

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

T-SQL Select and count from different table?

I have a table (Threads) containing a field (id). I would like to select every row from Threads, as well as the number of rows in the table Posts where the field Posts.thread is the same as Threads.id.
How can this be done in SQL?
(Something like this pseudo-SQL: SELECT *, COUNT(* FROM Posts WHERE Posts.id=Threads.id) FROM Threads)
Sure - something like this?
SELECT
t.ThreadID,
(SELECT COUNT(*) FROM dbo.Posts p WHERE p.ThreadID = t.ThreadID)
FROM
dbo.Threads t
SELECT t.id, COUNT(p.thread)
FROM Threads AS t
LEFT OUTER JOIN Posts AS p
ON t.id = p.thread
GROUP BY t.id

Resources