Two table join Postgres count child rows - sql

I have two tables: posts and replies.
The post table contains these columns
postid | forumName | title | content
The replies table contains these columns
replyid | content | postid
I would like to have a sql query that joins these tow tables and returns for each Forum
forumName | Total Number of Posts | Total Number of Replies
This is hard as the two tables are linked using postId.
select forum, count(id) as postsNum
from posts
group by forum
order by postsNum desc

Are you looking for this:
select p.forum
,count(distinct id) as posts
,count(r.replyid) as replies
from posts p
inner join replices r
on p.postid = r.postid
group by p.forum

Related

Joining Tags as an Array

I have this:
posts
- id
- title
- ...
tags
- pid (fk to posts.id)
- name
And I would like to join my posts so that I have an array of tags in my posts record:
id | title | | tags
___|_______|_____|_____________________
1 | me | ... | ["you", "him"]
2 | you | ... | ["him", "something"]
...
How can I accomplish this. I don't want to over fetch, either.
Here is what I have so far:
CREATE OR REPLACE VIEW posts_tags AS
SELECT posts.* FROM posts
JOIN array_agg(
SELECT tags.name FROM tags
WHERE posts.id = tags.pid
)
Not sure how the array part works in SQL or how to finish this query.
The query with fixed syntax:
SELECT *
FROM posts p
JOIN (
SELECT pid AS id, array_agg(name) AS post_tags
FROM tags
GROUP BY pid
) t USING (id);
It's faster to aggregate before you join.
For a small selection of posts, a LATERAL subquery, or a correlated subquery in the SELECT list can be faster. See:
Multiple array_agg() calls in a single query

SQL: how to select result of joined tables with limit based on main table rows quantity?

I have a table, movies.
+----+------+
| id | name |
+----+------+
And I have another table, genres.
+----+------+----------+
| id | name | movie_id |
+----+------+----------+
Guess, that every movie record have 3 genre records binded by movie_id.
I need to get 10 movie records with ALL genre records joined.
My query is:
select *
from movies
left join genres on movies.id = genres.movie_id
limit 10;
Result is 10 rows, but I want to get 10 movie rows with ALL genre rows joined = 30 rows.
What query should be?
you can select only 10 movies in a subquery:
select * from
(select * from movies limit 10) movies1
left join genres on movies1.id = genres.movie_id
Using GROUP BY you can retrieve one row pre movie with concatinated genres:
select movies.id, movies.name, GROUP_CONCAT(genres.name) genres
from movies
left join genres on movies.id = genres.movie_id
group by movies.id, movies.name
limit 10;
SQL group by fiddle

Get latest child row with parent table row

Two tables posts and comments. posts has many comments (comments has post_id foreign key to posts id primary key)
posts
id | content
------------
comments
id | post_id | text | created_at
-------------------------------
I need all posts, its content, and latest comment (based on max(created_at), and its text.
I can get upto created_at using this
with comment_latest as (select
post_id,
max(created_at) as latest_commented_at
from comments
group by 1)
select
posts.id,
posts.content,
comment_latest.latest_commented_at
from posts
left join comment_latest on comment_latest.post_id = posts.id
order by posts.id desc
limit 10
But I want the text of the comment as well.
You can use the Postgres extension distinct on:
select distinct on (p.id) p.* c.*
from posts p left join
comments c
on p.id = c.post_id
order by p.id desc, c.created_at desc
limit 10;
This sorts the data by the order by clause, returning the first row based on the keys in the distinct on.

SQL query (Join without duplicates)

I have tables users and topics. Every user can have from 0 to several topics (one-to-many relationship).
How I can get only those users which have at least one topic?
I need all columns from users (without columns from topics) and without duplicates in table users. In last column I need number of topics.
UPDATED:
Should be like this:
SELECT user.*, count(topic.id)
FROM ad
LEFT JOIN topic ON user.id = topic.ad
GROUP BY user.id
HAVING count(topic.id) > 0;
but it takes 0 result. But it should not be 0.
Firstly you need to have your two tables, because you have left limited information about your table structure I will use an example to explain how this works, you should then be able to easily apply this to your own tables.
Firstly you need to have two tables (which you do)
Table "user"
id | name
1 | Joe Bloggs
2 | Eddy Ready
Table "topic"
topicid | userid | topic
1 | 1 | Breakfast
2 | 1 | Lunch
3 | 1 | Dinner
Now asking for a count against each user is done using the follwing;
SELECT user.name, count(topic.topicid)
FROM user
INNER JOIN topic ON user.id = topic.userid
GROUP BY user.name
If you use a left join, this will include records from the "user" table which does not have any rows in the "topic" table, however if you use an INNER JOIN this will ONLY include users who have a matching value in both tables.
I.e. because the user id "2" (which we use to join) is not listed in the topic table you will not get any results for this user.
Hope that helps!
use inner join and distinct
select distinct user_table.id
from user_table
inner join topics_table on topic_table.user_id = user_table.id
select u.id
, u.name
, count(b.topicName)
from user u
left join topic t on t.userid = u.id
group by u.id, u.name
You can select topic number per user and then join it with user data. Something like this:
with t as
(
select userid, count(*) as n
from topic
group by userid
)
SELECT user.*, t.n
FROM user
JOIN t ON user.id = t.userid

SQL: How to select a count of multiple records from one table, based on records in a different table?

Let's say I have two tables:
TABLE A
MessageID | Message
1 | Hello
2 | Bonjour
etc..
TABLE B
CommentID | MessageID | Comment
1 | 2 | This is a comment to someone saying Bonjour
2 | 2 | This is another comment to Bonjour
What I'm trying to do is run one query that pulls all the records from Table A ("the messages") along with a count of all the comments for each message from Table B.
The result would be:
Hello - 0 comments
Bonjour - 2 comments
I know this is probably some combination of using a join with a count(*), but I can't seem to hit on just the right syntax.
Any ideas?
For a message based approach:
SELECT message, count(commentID)
FROM tableA LEFT JOIN tableB ON tableA.messageID = tableB.messageID
GROUP BY message
You'll want a LEFT JOIN to include records in Table A that don't have any comments in Table B.
Give this a try:
SELECT a.MessageID, COUNT(*)
FROM TABLEA a
JOIN TABlEB b
ON b.MessageID = a.MessageID
GROUP BY a.MessageID
Just an addendum to what's already posted since, if you're using this for a scripting language, you'll likely need named columns for everything in the SELECT list:
SELECT tableA.messageID, message, count(commentID) AS commentCount
FROM tableA LEFT JOIN tableB ON tableA.messageID = tableB.messageID
GROUP BY message
Something like this:
SELECT MessageID, COUNT(CommentID)
FROM "TABLE B"
GROUP BY MessageID
If you need Message in addition to MessageID, you should be able to do something like this:
SELECT MessageID, MIN(Message), COUNT(CommentID)
FROM "TABLE A" LEFT JOIN "TABLE B" USING (MessageID)
GROUP BY MessageID
(Oracle syntax, probably same for MSSQL)