Use Two Conjection Table in a Single Query? - php

I am creating a blog post system in which I am using following table structure :-
blog_category
category_id, category_name, enabled, created_date
blog_post
post_id,title,article, author_id,date_published
post_category
category_id, post_id
blog_tag
tag_id, tag_name
post_tag
id,tag_id,post_id
post_related
id, post_id, post_related_id
blog_comment
comment_id, post_id, is_reply_to_id,comment,user_id
My questions ARE:
How to get this type of output in a single query
title,author_name,article,total_comments,post_tags
How to insert records in post_related table to show the related post when select a particular post.
I trying like this but it show unknown column bp.post_id
CREATE PROCEDURE blog_get_posts_in_category(
IN inCategoryId INT, IN inShortPostDescriptionLength INT,
IN inPostsPerPage INT, IN inStartItem INT)
BEGIN
PREPARE statement FROM
"SELECT bp.post_id, bp.title,(select count(*) from blog_comment where post_id=bp.post_id) as total_comments,
IF(LENGTH(bp.article) <= ?,
bp.article,
CONCAT(LEFT(bp.article, ?),
'...')) AS article,
DATE_FORMAT(bp.date_published,'%d %M %Y at %h:%i:%s %p') as date_published, bp.banner_image,ba.display_name
FROM blog_post bp
INNER JOIN blog_post_to_category bpc
ON bpc.post_id = bp.post_id
INNER JOIN blog_author ba
ON ba.id = bp.author_id
WHERE bpc.category_id = ? and enabled=1
ORDER BY bp.post_id DESC
LIMIT ?, ?";
SET #p1 = inShortPostDescriptionLength;
SET #p2 = inShortPostDescriptionLength;
SET #p3 = inCategoryId;
SET #p4 = inStartItem;
SET #p5 = inPostsPerPage;
EXECUTE statement USING #p1, #p2, #p3, #p4, #p5;
END$$

Q1:
This is not really clear from the question, so lets suppose that
- blog_author table looks like
author_id, author_name, ...
- total_comments is the number of all the comments belonging to a blog post.
- post_tags could be one string containing all the post_tags related to a blog post (e.g. as a csv).
Then, a query could be:
SELECT
bp.post_id as pid, bp.title, a.author_name, bp.article, bc.total_comments, pt.post_tags
FROM blog_post bp
LEFT JOIN blog_author a ON bp.author_id=a.id
LEFT JOIN (
SELECT post_id, COUNT(*) as total_comments
FROM blog_comment
GROUP BY post_id) bc
ON bc.post_id=bp.post_id
LEFT JOIN (
SELECT post_id, GROUP_CONCAT(DISTINCT tag_id SEPARATOR ',') AS post_tags
FROM post_tag
GROUP BY post_id) pt
ON pt.post_id=bp.post_id;
Q2:
Here, the query seems to be OK. I even tried it myself and I got no error message about the bp.post_id. Just one idea that the subquery with the total_comments could be avoided using the join (with the blog_comment table) that I show in my previous query.
But this procedure/query does something different than the question supposed to be about. (It does not even have any reference to the blog_related table.)

Related

MySQL How to query multiple tables using different constraints?

How to query multiple tables using different constraints?
For example, limiting results to 1 row from the first table, but getting all results from the second and then ordering those results by unique id ASC.
For example, something like this:
SELECT p.entry_id AS post_id,
p.topic AS post_topic,
p.body AS post_body,
r.reply AS post_reply
FROM
#should get only one row
(SELECT entry_id, topic, body FROM entry_posts WHERE entry_id = {$id} LIMIT 1) AS p
FULL JOIN
#should get all rows with this entry_id and order them
(SELECT reply, FROM entry_replies WHERE entry_id = {$id} ORDER BY id ASC) AS r
ON p.entry_id = r.entry_id
In this case both tables have a column called entry_id that contain the same values.
SELECT p.entry_id AS post_id,
p.topic AS post_topic,
p.body AS post_body,
r.reply AS post_reply
FROM entry_posts p
LEFT OUTER JOIN entry_replies r ON r.entry_id = p.entry_id
WHERE p.entry_id = {$id}
ORDER BY r.id

Select columns, count(id) from table and reference table

I have a query where I want to fetch columns from two tables (a parent and reference table), as well as select total number of categories of each post on the reference table
articles
id
title
poster
pdate
content
categories
id
name
cats_rel (Relationship table with Foreign Key constraints)
id
pid
cat_id
I want to fetch list of posts with one category id and total number of categories for each post.
I use this to fetch the required data, but it is slow. Is there a better way to get it faster?
SELECT cc.id, title, poster, pdate, content, js.pid, js.sno
FROM articles cc LEFT JOIN
(SELECT pid, cat_id, count(cat_id) as sno FROM cats_rel GROUP BY pid)js
ON js.pid = cc.id WHERE cc.status='approved' ORDER BY cc.id DESC
You don't need a inner query.
SELECT cc.id, title, poster, pdate, content, js.pid, count(js.cat_id) as sno
FROM articles wp
LEFT JOIN cats_rel js ON js.pid = cc.id
WHERE wp.status='approved'
group by cc.id, title, poster, pdate, content, js.pid
ORDER BY cc.id DESC
I would rewrite the query using a correlated subquery
SELECT cc.id, cc.title, cc.poster, cc.pdate, cc.content,
(SELECT COUNT(*)
FROM cats_rel js
WHERE js.pid = cc.id
) as sno
FROM articles cc
WHERE cc.status = 'approved'
ORDER BY cc.id DESC;
(It seems that js.pid is redundant in the select list.)
For this query, you want two indexes: articles(status, id desc) and cats_rel(pid).

How to join another table?

Right now I'm using UNION to get records from multiple tables, which is fine. But I want to include the author for the specific post.
$run = mysql_query("
SELECT article_id AS id, title, smalldesc, hits, coverpic AS picture, timestamp, member_id AS mid, type FROM articles
UNION
SELECT video_id AS id, titel, smalldesc, hits, ytid AS picture, timestamp, member_id AS mid, type FROM videos
UNION
SELECT picture_id AS id, title, smalldesc, hits, coverpic AS picture, timestamp, member_id AS mid, type FROM pictures ORDER BY timestamp DESC LIMIT ".$postnumbers." OFFSET ".$offset."
") or die(mysql_error());
I want the following select to be a part of the code above:
SELECT member_id, picture, fname, lname FROM members WHERE member_id='mid'
I want to get the member from each post, so I can print out who made the post.
You've got, as you asked, to join the members table to the three part queries of the UNION:
SELECT
a.article_id AS id,
a.title,
a.smalldesc,
a.hits,
a.coverpic AS picture,
a.timestamp,
a.member_id AS mid,
a.type,
m.picture,
m.fname,
m.lname
FROM
articles a
INNER JOIN
members m
ON
a.member_id = m.member_id
UNION
SELECT
v.video_id,
v.titel,
v.smalldesc,
v.hits,
v.ytid AS picture,
v.timestamp,
v.member_id AS mid,
v.type
m2.picture,
m2.fname,
m2.lname
FROM
videos v
INNER JOIN
members m2
ON
v.member_id = m2.member_id
UNION
...
to get this information.

mysql activity feed of people you follow (likes, comments, etc.)

Some background:
I'm developing kind of social network.
There is DB with next tables:
users(user_id, user_name, password, etc.)
posts(post_id, user_id, post_text, post_data, etc.)
followings(relationship_id, follower_id, following_id, added_date_time)
likes(like_id, user_id, post_id, added_date_time)
comments(comment_id, user_id, post_id, comment_text, added_date_time)
The question is:
How can I implement query which would fetch information about what my followings(people I follow) have did recently (e.g. someone liked/commented something) and order this array of info by date_time?
Can I make it using one query? Or I'll have to make multiple queries and handle all this stuff in PHP by myself?
What is the best approach?
You can normalize the output in a select union like this one
SELECT t.type, t.user_id, u.user_name, t.type_id, t.post_id, t.text, t.added_date_time
FROM (
SELECT 'posts' as type, p.user_id, p.post_id as type_id, p.post_id, post_text as text, p.added_date_time
FROM posts p
JOIN followings f ON (f.following_id = p.user_id)
WHERE f.follower_id = #user
UNION
SELECT 'comments' as type, c.user_id, c.comment_id as type_id, c.post_id, comment_text as text, c.added_date_time
FROM comments c
JOIN followings f ON (f.following.id = c.user_id)
WHERE f.follower_id = #user
UNION
SELECT 'likes' as type, l.user_id, l.like_id as type_id, l.post_id, post_text as text, l.added_date_time
FROM likes l
JOIN followings f ON (f.following.id = c.user_id)
JOIN posts p ON (l.post_id = p.post_id)
WHERE f.follower_id = #user
) as t
JOIN users u ON (t.user_id = u.user_id)
ORDER BY added_date_time DESC;
So you can get all the data in only one query.
I hope it works fine for you.
You have to seperate at least the queries for likes and comments, since the result set is different.
Otherwise your query would look e.g. like:
SELECT comment_id,comment_text,added_date_time FROM comments WHERE user_id IN (SELECT following_id FROM followings WHERE follower_id={{USER_ID_FROM_USER}}) ORDER BY added_date_time
This will get you the comments from people which are followed by {{USER_ID_FROM_USER}} ordered by date.

MySQL multiple table SELECT

I have three tables:
Table(attribute1, attribute2...);
---------------------------------
Users(iduser, username)
Link(idlink, title, userid)
Comment(idcomment, content, linkid, userid)
How to select:
Link title, with corresponding username and number of comments?
I'm currently doing like this:
Q1-Select links (SELECT * FROM `links`)
Q2-Extract usernames from previous query(Q1) - (SELECT username FROM `user` WHERE iduser=Q1.userid
Q3-Extract number of comments from Q1 by id (SELECT COUNT(*) as comments FROM `comment` WHERE linkid='Q1.idlink')
I believe we can do this in much more optimized way. I got idea how to get Link with corresponding username but I got stuck when I need to count comments.
SELECT iduser, username, Link.title, COUNT(idcomment)
FROM Users
LEFT JOIN Link ON (iduser = userid)
LEFT JOIN Comment ON (linkid = idlink)
GROUP BY iduser, idlink
Note that your Comment table is somewhat badly designed - the 'userid' field is not necessary, and can actually lead to situation where you've got a cross-linked record. e.g. a Comment belonging to user A might could be linked to a Link record belonging to user B.
It is good practice to get into the habit of putting the fields you want into both the SELECT and GROUP BY clauses, that way it won't come as such a shock when you have to use an RDBMS that insists on it.
SELECT
`l`.`idlink`,
`l`.`title`,
`u`.`username`,
COUNT(`c`,`idcomment`) AS `comment_count`
FROM `links` `l`
INNER JOIN `users` `u`
ON `l`.`userid` = `u`.`iduser`
LEFT JOIN `comments` `c`
ON `l`.`idlink` = `c`.`linkid`
GROUP BY
`l`.`idlink`,
`l`.`title`,
`u`.`username`
SELECT
l.idlink
, l.title
, l.userid
, u.iduser
, u.username
, c.idcomment
, c.content
FROM Link AS l
JOIN Users AS u ON u.iduser=l.userid
JOIN Comment AS c ON c.linkid=l.idlink

Categories