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

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

Related

Insert multiple values with the same foreign key

I have two tables that reference each other:
CREATE TABLE Room
room_id INTEGER PRIMARY KEY,
room_name TEXT UNIQUE NOT NULL;
CREATE TABLE Item
item_id INTEGER PRIMARY KEY,
room_id INTEGER,
item_name TEXT,
FOREIGN KEY (room_id) REFERENCES Room (room_id) ON UPDATE RESTRICT ON DELETE RESTRICT;
Now I want to add a new room and add a few dozen items to go into it.
INSERT INTO Room(room_name) VALUES ('Living Room');
Let's say I don't know how many rooms there are, and I just want to put stuff into the living room. To do that, I need to select the right room_id. For a single item this is not too bad:
INSERT INTO Item(room_id, item_name)
SELECT room_id, 'Couch' AS item_name FROM Room WHERE room_name = 'Living Room';
But what if I want to insert a bunch of values simultaneously. I tried using last_insert_rowid, but that does not treat the entire INSERT as a single transaction. In other words, the last ID keeps incrementing
INSERT INTO Item (room_id, item_name)
VALUES
(last_insert_rowid(), 'Chair'),
(last_insert_rowid(), 'TV'),
(last_insert_rowid(), 'Carpet');
I would like to avoid having to use the SELECT on each new row. Is there a way to insert multiple values into Item, while referencing the last known room_id in Room?
Something in the nature of a CROSS JOIN would likely be very useful, but I don't know how to get the constants to behave in that case
The end result I am looking for is for Room to look like this:
room_id | room_name
--------+-----------
1 | Living Room
And Item like this:
item_id | room_id | item_name
--------+---------+-----------
1 | 1 | Chair
2 | 1 | TV
3 | 1 | Carpet
You can use a CROSS join of the id that you get from the new room to a CTE that returns the items that you want to insert:
WITH cte(item_name) AS (VALUES ('Chair'), ('TV'), ('Carpet'))
INSERT INTO Item (room_id, item_name)
SELECT r.room_id, c.item_name
FROM Room r CROSS JOIN cte c
WHERE r.room_name = 'Living Room';
See the demo.
If you are using a version of SQLite that does not support CTEs use UNION ALL in a subquery:
INSERT INTO Item (room_id, item_name)
SELECT r.room_id, c.item_name
FROM Room r
CROSS JOIN (
SELECT 'Chair' item_name UNION ALL
SELECT 'TV' UNION ALL
SELECT 'Carpet'
) c
WHERE r.room_name = 'Living Room';
See the demo.

How to query on SQLite with relational tables?

I have a table called "ASSIGNMENTS" and another table called "COURSE". My "COURSE TABLE" has a column called "limit" in which i store the limit of students that can be assigned to a COURSE.
ASSIGNMENTS
| id(PK) | course_code(FK references course.id) |
COURSE
| id(PK)| name | limit(INT) |
So basically what i have to do is return the limit of each course MINUS the amount of ASSIGNMENTS that the course has had.
So if my Course "Math" has a limit of 30 assignments, and i have 5 assignments with course_code
for "Math" in my assignments table, i should return 25. How can i achieve this? i'm having a very hard time decoding this. (very new to sql)
I'm writing my code using sqlite, this is what i have so far:
SELECT DISTINCT
c.name AS 'COURSE NAME', c.limit as 'AVAILABLE'
FROM COURSE c
INNER JOIN ASSIGNMENTS i
ON c.id=i.course_code
And right there i just don't know how to make it return the limit value MINUS the amount of assignments for each Course. Any help or examples of sorts will be appreciated, thank you.
You can group by course and subtract the number of assignments from limit:
SELECT c.id, c.name, c.`limit`,
c.`limit` - COUNT(*) available
FROM COURSE c INNER JOIN ASSIGNMENTS a
ON c.id = a.course_code
GROUP BY c.id, c.name
Or, with a correlated subquery:
SELECT c.*,
c.`limit` - (SELECT COUNT(*) FROM ASSIGNMENTS a WHERE a.course_code = c.id) available
FROM COURSE c
See a simplified demo.

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

SQL Select Query Asp.Net

I have a product page on a webpage that shows categories of products. This is done with a listview populated from a database. The issue that I have is that the main supplier has demanded that their products are first in the category list. So what I need to do is run a query that will return the results, display those two categories first and then display the rest alphabetically.
So I've been trying to do this using a UNION ALL query like this:
SELECT cat, cat_id, image FROM prod_categories WHERE cat_id = 19 OR cat_id = 65
UNION ALL
SELECT cat, cat_id, image FROM prod_categories WHERE cat_id <> 19 AND cat_id <> 65
I thought with a union like this it would display the results of the first select query first, but it's not doing that.
I can add an 'order by cat' clause on the end, but obviously that only displays them in the correct order if the two categories I want to display come first alphabetically, which they don't.
If anyone has any ideas how to do this it would be greatly appreciated.
Thanks
How about this:
SELECT cat, cat_id, image FROM prod_categories
order by case when cat_id in (19, 65) then 1 else 2 end, cat_id
Cuts out the need to UNION altogether. Might even produce a more efficient execution plan (possibly...).
(using Transact-SQL for SQL Server - the exact syntax may have to be tinkered for MySql etc)
Try something like this.
SELECT cat, cat_id, image, 1 as [srt]
FROM prod_categories WHERE cat_id = 19 OR cat_id = 65
UNION ALL
SELECT cat, cat_id, image, 2 as [srt]
FROM prod_categories WHERE cat_id <> 19 AND cat_id <> 65
ORDER BY srt ASC, cat_id
Don't hard-code this into your query. What happens when the next supplier wants to come second? Or last? For that matter, you may want to list categories in some sort of "group", anyways.
Instead, you should be using an ordering table (or multiple). Something simple to get you started:
CREATE TABLE Category_Order (categoryId INTEGER -- fk to category.id, unique
priority INTEGER) -- when to display category
Then you want to insert the values for the current "special" categories:
INSERT INTO Category_Order (categoryId, priority) VALUES (19, 2147483647), (65, 0)
You'll also need an entry for rows that are not currently prioritized:
INSERT INTO Category_Order (categoryId, priority)
SELECT catId, -2147483648
FROM prod_categories
WHERE catID NOT IN (19, 65)
Which can then be queried like this:
SELECT cat, cat_id, image
FROM prod_categories
JOIN Category_Order
ON category_id = cat_id
ORDER BY priority DESC, cat
If you write a small maintenance program for this table, you can then push re-ordering duties off onto the correct business department. Reordering of entries can be accomplished by splitting the difference between existing entries, although you'll want a procedure to re-distribute if things get too crowded.
Note that, in the event your db supports a clause like ORDER BY priority NULLS LAST, the entries for non-prioritized categories are unnecessary, and you can simply LEFT JOIN to the ordering table.

Resources