SQLite Random sub query result - sql

I have 2 SQLite tables, table_a and table_b. Table A contains a list of categories and Table B contains a list of words.
I'm trying to get all categories from table_a and a random word from table_b for each category. The below query allows me to get 1 result per category that has the lowest id in table_b.
SELECT table_a.category_id, table_a.category, subQuery.word, subQuery.word_category, subQuery.word_id
FROM table_a,
(SELECT *
FROM table_b
GROUP BY category_id
HAVING MIN(word_id)
) subQuery
WHERE subQuery.category_id = table_a.category_id;
Is there a way to return 1 random result from table_b for each category in table_a instead of the value with the lowest id?
I'm not really having much luck finding an answer online. Any help would be appreciated.

You can use a window function to pick one row per category:
select
c.category_id, c.category,
w.word, w.word_category, w.word_id
from categories c
left join
(
select
word, word_category, word_id, category_id,
row_number() over (partition by category_id order by random()) as rn
from words
) w on w.category_id = c.category_id and w.rn = 1
order by c.category_id;
Demo: https://dbfiddle.uk/?rdbms=sqlite_3.27&fiddle=bbbe8541adf2d4f62883b2a1c36aaa41
Update
You say that the query does not work in SQLite 3.8. We can only suspect a bug here, because we see it works in SQLite 3.27.
Here is another approach that may work for you. I am selecting a random word ID with each category using a LIMIT subquery. Then I join the word data.
select c.category_id, c.category, w.word, w.word_category, w.word_id
from
(
select
c.category_id, c.category,
(
select word_id
from words w
where w.category_id = c.category_id
order by random()
limit 1
) as word_id
from categories c
) c
join words w on w.word_id = c.word_id
order by c.category_id;
Demo: https://dbfiddle.uk/?rdbms=sqlite_3.27&fiddle=7630068aa3ba71e70b7a6bddd83047dc

Related

One query to return multiple rows for multiple values with limit

Assume you have database with author and book tables.
I want to get 10 most recent books for each author.
Normally it is N+1 SQL queries.
1 to get all authors: SELECT * FROM author -> N rows.
N to get their most recent books: SELECT * FROM book WHERE authorId=$1 ORDER BY releaseDate LIMIT=10.
I want to have only 1 instead of N queries, to get 10 most recent books for list of authors. ( maybe it does not make sense in case of performance / but I do not know )
Like this -> completely wrong, because it returns most recent books in table not per author SELECT * FROM book WHERE authorId = ANY($1) ORDER BY releaseDate DESC LIMIT 10
Is there a way to make SQL query to return most recent books for list of authors? Thanks.
You can try using row_number()
select * from
(
select *,row_number() over(partition by a.authorid order by releasedate desc) as rn
from author a join books b on a.authorid=b.authorid
)f where rn<=10
An alternative to window functions using lateral join:
select a.authorid, b.*
from author a
cross join lateral
(
select * from books
where books.authorid = a.authorid
order by releasedate desc
limit 10
) b;

How to fetch all rows from table with count of specific group?

I have a simple table like this
spatialite> select id, group_id, object_id, object, param from controlled_object;
1|1|150|nodes|0.5
2|1|186|nodes|0.5
3|2|372|nodes|1.0
The second column is group_id. I want to retrieve all entries from the table, plus the count of the group.
1|1|150|nodes|0.5|2
2|1|186|nodes|0.5|2
3|2|372|nodes|1.0|1
I thought a cross join would be the way to go
SELECT
*
, cj.cnt
FROM
controlled_object
CROSS JOIN (
SELECT
COUNT(DISTINCT group_id) AS cnt
FROM
controlled_object
) AS cj
But that gives me
1|1|150|nodes|0.5|2|2
2|1|186|nodes|0.5|2|2
3|2|372|nodes|1.0|2|2
How do I fetch all rows from table including the count of a specific group?
Join source data with counters, grouped by group_id
select c.id, c.group_id, c.object_id, c.object, c.param,cnt from controlled_object c join
(select group_id,count(*) cnt from controlled_object group by group_id) p on c.group_id =p.group_id ;
Not very good idea for big tables
Sqlite is not very good idea for big tables at all :-)
You can compute the count with a correlated subquery:
SELECT id,
group_id,
object_id,
object,
param,
(SELECT count(*)
FROM controlled_object AS co2
WHERE group_id = controlled_object.group_id)
FROM controlled_object;

order results from table A by total number of related rows form table B

i have two tables, "topics" (table A) and "topic votes" (table B). each "topic" has multiple "topic votes". i need to select rows from "topics" and order them by the total number of related "topic votes".
how is it possible in sqlite? do i need to create a view for this, or is it possible to solve with JOIN?
If you don't need the votes you can just put a correlated query in the order by:
select t.*
from topics t
order by (select count(*) from topic_votes tv where t.topic = v.topic) desc;
Normally, you would want the number of votes in the result set as well. A simple method is to move the subquery to the select clause:
select t.*,
(select count(*) from topic_votes tv where t.topic = v.topic
) as votes
from topics t
order by votes desc;
Assuming you have a PK id in topics table and FK topic_id in topic votes table:
select
t.*
from topics t
left join topic_votes v on t.id = v.topic_id
group by t.id
order by count(v.topic_id) desc;

oracle select from 2 table restrict from 1 table and order by another

I have book and recipient table. I want to select maximum 20 rows order by recipient table's membershipdate column. After I got it, i want to order it by book table's id column. I wrote that sql. Is there any way to do this with less code?
SELECT *
FROM ( SELECT *
FROM ( SELECT b.*
FROM book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115
ORDER BY r.membershipdate DESC
)
WHERE ROWNUM <= 20
)
ORDER BY ID DESC
You can remove one layer of select:
SELECT *
FROM (
SELECT b.*
FROM book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115
ORDER BY r.membershipdate DESC
)
WHERE ROWNUM <= 20
ORDER BY id DESC;
Selecting b.* isn't normally a good, idea, it's better to specify the columns you actually want, even if you do really want them all - to make sure you get them in the order you expect.
You can also look at the row_number() analytic function in place of rownum, but that will give you slightly more code - not that it should matter, the effectiveness and efficiency of the query it rather more important that its length.
Doesn't this work?
SELECT * FROM
(SELECT b.*
FROM book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115 ORDER BY r.membershipdate DESC
) WHERE ROWNUM <= 20
ORDER BY ID DESC
In oracle you can use ROW_NUMBER() function to reduce the code as you want -
and you can even mix your Order columns also.
select * from (
select b.*,row_number() over (order by r.membershipdate desc) cnt
from book b
JOIN recipient r ON r.id = b.recipient_id
WHERE b.bookno = 115
order by cnt,b.id desc
) where cnt<=20;

MYSQL top N rows from multiple table join

Like, there is top keyword in sql server 2005, how to select top 1 row in mysql if i have join on multiple table & want to retrieve extreme of each ID/column. Limit restricts the no. of row returns so it can't solve my problem.
SELECT v.*
FROM document d
OUTER APPLY
(
SELECT TOP 1 *
FROM version v
WHERE v.document = d.id
ORDER BY
v.revision DESC
) v
or
SELECT v.*
FROM document d
LEFT JOIN
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY v.id ORDER BY revision DESC)
FROM version
) v
ON v.document = d.id
AND v.rn = 1
The latter is more efficient if your documents usually have few revisions and you need to select all or almost all documents; the former is more efficient if the documents have many revisions or you need to select just a small subset of documents.
Update:
Sorry, didn't notice the question is about MySQL.
In MySQL, you do it this way:
SELECT *
FROM document d
LEFT JOIN
version v
ON v.id =
(
SELECT id
FROM version vi
WHERE vi.document = d.document
ORDER BY
vi.document DESC, vi.revision DESC, vi.id DESC
LIMIT 1
)
Create a composite index on version (document, revision, id) for this to work fast.
If I understand you correctly, top doesn't solve your problem either. top is exactly equivalent to limit. What you are looking for is aggregate functions, like max() or min() if you want the extremes. for example:
select link_id, max(column_a), min(column_b) from table_a a, table_b b
where a.link_id = b.link_id group by link_id