Getting specific number of rows from the same table using different criteria - sql

I have news table from where I need to select specific number or rows - for example 3 news from 4 categories. There should always be 3 news from each category (table is full of contents - it's not the matter). I want to select only the newest ones.
Suppose my table contains 5 fields: id_news, news_title, news_desc, news_date, id_news_category.
Edit: Looks like those previous questions can't help me because what I really need to do is to select top N from range of categories id, for eg. 1-6, 7-13, 14-20 etc.
I'm looking for the best (most efficient) way to accomplish this. Is it UNION operator:
SELECT id_news, news_title
FROM news
WHERE id_news_category BETWEEN 1 AND 6
ORDER BY news_date DESC
LIMIT 3
UNION
SELECT id_news, news_title
FROM news
WHERE id_news_category BETWEEN 7 AND 13
ORDER BY news_date DESC
LIMIT 3
UNION
SELECT id_news, news_title
FROM news
WHERE id_news_category BETWEEN 14 AND 20
ORDER BY news_date DESC
LIMIT 3
UNION
SELECT id_news, news_title
FROM news
WHERE id_news_category BETWEEN 21 AND 27
ORDER BY news_date DESC
LIMIT 3
I know above UNION may have quite other form - with single order. Not really sure if it has performance impact. I'm using PSql 8.4 and Symfony so I will need to use QueryBuilder or maybe just native query.

Related

SQLite: How to fetch the ORDER index of COUNT

I have an SQLite database that contains a list of user messages in a group.
And I want to get a user's "rank" by counting the number of messages they had sent.
Currently I'm doing this
SELECT user_id, COUNT(*) as count
FROM message
group by user_id
ORDER BY count DESC
It'd return something like this:
-
user_id
count
1
2072040132
61877
2
1609505732
40514
3
1543287045
34735
4
203349203
30570
5
842634673
29651
6
1702633101
29185
7
1978947042
27728
8
1929648593
27025
9
1069841429
17944
10
1437208364
17344
11
...
...
Like user 1609505732 is top 2, and 1702633101 is top 6.
But my database has more than 2 million rows, and this is too slow having to fetch all of the list.
I was wondering if there are any way that I can fetch only the order of it.
Like this:
-
user_id
order
count
1
1702633101
6
61877
And the user with id=1702633101 is top 6. That'd be a lot faster.
Thanks for spending time on my question, I can't seem to find the answer anywhere on the internet.
To improve query speed, I'd consider physicalising the aggregate view, example below:
CREATE Table as tbl_aggregate()
Id INTEGER PRIMARY KEY AUTOINCREMENT
, user_id NVARCHAR
, count INT;
INSERT INTO tbl_aggregate
SELECT user_id, COUNT(*) as count
FROM message
group by user_id
ORDER BY count DESC;
Select * from tbl_aggregate
Where Id = 6
Select top 10 * from tbl_aggregate

Fetching a minimum of N rows, plus all peers of the last row

I have a sample table named assets which looks like this:
id
name
block_no
1
asset1
2
2
asset2
2
3
asset3
3
There can be any number of assets in a specific block. I need a minimum of 100 rows from the table, and containing all the data from the block_no. Like, if there are 95 rows to block_no 2 and around 20 on block_no 3, I need all 20 of block_no 3 as if I am fetching data in packets based on block_no.
Is this possible and feasible?
Postgres 13 or later
There is a dead simple solution using WITH TIES in Postgres 13 or later:
SELECT *
FROM assets
WHERE block_no >= 2 -- your starting block
ORDER BY block_no
FETCH FIRST 100 ROWS WITH TIES;
This will return at least 100 rows (if enough qualify), plus all peers of the 100th row.
If your table isn't trivially small, an index on (block_no) is essential for performance.
See:
Get top row(s) with highest value, with ties
Older versions
Use the window function rank() in a subquery:
SELECT (a).*
FROM (
SELECT a, rank() OVER (ORDER BY block_no) AS rnk
FROM assets a
) sub
WHERE rnk <= 100;
Same result.
I use a little trick with the row type to strip the added rnk from the result. That's an optional addition.
See:
PostgreSQL equivalent for TOP n WITH TIES: LIMIT "with ties"?

How to get top 10 from one column and sort by another column in hive?

I want to find top 10 title with high number of user ids. So I used query like
select title,count(userid) as users from combined_moviedata group by title order by users desc limit 10
But i need to sort them based on title, I tried this query
select title,count(userid) as users from combined_moviedata group by title order by users desc,title asc limit 10
But it doesnot sort them. Merely returned same results. How to do this
The answer from #KaushikNayak is very close to what I'd consider the "right" answer.
At one level, work out what your top 10 records are
At a different level, sort them by a different field
The only thing I'd say is that if the 10th and 11th most common titles are tied for the same count, they should generally also be included in the results. This is a RANK().
WITH
ranked_titles AS
(
SELECT
RANK() OVER (ORDER BY COUNT(*) DESC) frequency_rank,
title
FROM
combined_moviedata
GROUP BY
title
)
SELECT
*
FROM
ranked_titles
WHERE
frequency_rank <= 10
ORDER BY
title
;
http://sqlfiddle.com/#!6/7283c/1
Note that in the example linked, 12 rows are returned. That is because 4 titles are all tied for the 9th most frequent, and it is actually impossible to determine which two should be selected in preference over the others. In this case selecting 10 rows would normally be statistically incorrect.
title frequency frequency_rank
title06 2 9
title07 2 9
title08 2 9
title09 2 9
title10 3 6
title11 3 6
title12 3 6
title13 4 4
title14 4 4
title15 5 2
title16 5 2
title17 6 1
You could make use of a WITH clause
with t AS
(
select title,count(userid) as users from combined_moviedata
group by title
order by users desc limit 10
)
select * FROM t ORDER BY title ;

sqlite query unsorted result

I have list of Ids 31165,31160,31321,31322,31199,31136 which is dynamic.
When I run query
select id,name from master_movievod where id in(31165,31160,31321,31322,31199,31136);
I get following result
31136|Independence Day
31160|Planet of the Apes
31165|Mrs. Doubtfire
31199|Moulin Rouge
31321|Adult Movie 2
31322|Adult Movie 3
This is sorted list in ascending order.
I want the list in the same order which I give as input like
31165|Mrs. Doubtfire
31160|Planet of the Apes
31321|Adult Movie 2
31322|Adult Movie 3
31199|Moulin Rouge
31136|Independece Day
Without an order by clause, there's no guarantee on the order a database returns the results to you. SQLite, unfortunately, doesn't have something like MySQL's field for custom sorting, but you can jimmy-rig something with a case expression:
SELECT id, name
FROM master_movievod
WHERE id IN (31165, 31160, 31321, 31322, 31199, 31136)
ORDER BY CASE ID WHEN 31165 THEN 0
WHEN 31160 THEN 1
WHEN 31321 THEN 2
WHEN 31322 THEN 3
WHEN 31199 THEN 4
WHEN 31136 THEN 5
END ASC
Unfortunately, SQLite does not have an option like MySQL's FIELD for doing a custom ordering. You are left with two options. The first is that you could create a custom table containing the ordering you want and use that to sort. This option isn't very attractive. The second (and easier) option is to use ORDER BY CASE to achieve the order you want:
SELECT id, name FROM master_movievod
WHERE id IN (31165,31160,31321,31322,31199,31136)
ORDER BY
CASE id
WHEN 31165 THEN 0
WHEN 31160 THEN 1
WHEN 31321 THEN 2
WHEN 31322 THEN 3
WHEN 31199 THEN 4
WHEN 31136 THEN 5
END ASC

How do I average numbers according to each distinct ID in SQL?

I have CommentRating table that holds a foreign key to the DrinkId. I'm trying to get the average ratings for each DrinkId, and along with that I want to display the top three drinkIds that have the highest ratings.
commentRating drinkID
9 7
9 4
8 11
8 7
7 4
6 4
6 11
Here's the SQL I have so far, but I don't know how to change it.
Select TOP(3)(AVG(commentRating)),DISTINCT(drinkID)
FROM Comment order by commentRating desc
How do I average the ratings, select the drinks with the top three ratings, and return them in SQL?
You need to GROUP BY the result by the drinkID:
SELECT TOP 3 AVG(commentRating), drinkID
FROM Comment
GROUP BY drinkID
ORDER BY AVG(commentRating) DESC
I recommend you to read your favorite SQL documentation about details on GROUP BY. For T-SQL it is GROUP BY (Transact-SQL) on MSDN.
AVG is an aggregate function. Again, I'd recommend you to read some documentation on aggregate functions, in T-SQL it's in the MSDN library too.
For T-SQL:
select TOP 3 * from
(
select drinkID,avg(commentRating) avgCom
from Comment group by drinkID
) t order by avgCom DESC
For MySQL use LIMIT key word.