Joining Query with Multiple Tables Based on Values - php

Supposed I have a table posts like below:
| id | type | ref_id |
-----------+--------------+ ---------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 3 | 3 |
| 4 | 1 | 4 |
| 5 | 2 | 5 |
| 6 | 3 | 6 |
The type field is to join with other tables, so for example 1=blogs, 2=reviews, 3=photos, and each of them have respective fields, while ref_id is the ID of the blog/review/photo.
What i want to do is to show all latest posts (order by id DESC), while joining with all other tables based on the value of the type, but I want to do it with one query if possible. My no-good query is below:
SELECT * FROM posts
LEFT JOIN blogs ON posts.ref_id = blogs.ID AND posts.type = 1
LEFT JOIN reviews ON posts.ref_id = reviews.ID AND posts.type = 2
LEFT JOIN photos ON posts.ref_id = photos.ID AND posts.type = 3
ORDER BY posts.id DESC
The query doesn't result in anything. Another alternative is to loop the SELECT * FROM posts query and do another query based on the type, but if possible, I'd like to do it in 1 go.
Thanks for the help.

To see why your query does not return anything, consider moving the " AND posts.type = " from JOIN to WHERE clause. It will be:
select * from posts ... where type=1 and type=2 and type=3
Obviously, it will return no results. I know it is not the answer to your question, but I would consider a small change in your table definitions:
(table name: fields)
posts: id
blogs: id, post_id
reviews: id, post_id
photos: id, post_id
In this case you will be able to construct simpler and more traditional queries:
select * from posts
left join blogs on blogs.post_id = posts.id
left join reviews on reviews.post_id = posts.id
left join photos on photos.post_id = posts.id

I guess you want something like this:
( SELECT posts.*, blogs.*
FROM posts
JOIN blogs
ON posts.ref_id = blogs.ID
AND posts.type = 1
UNION ALL
SELECT posts.*, reviews.*
FROM posts
JOIN reviews
ON posts.ref_id = reviews.ID
AND posts.type = 2
UNION ALL
SELECT posts.*, photos.*
FROM posts
JOIN photos
ON posts.ref_id = photos.ID
AND posts.type = 3
)
ORDER BY posts.id DESC

Related

count total volume by category

I have 3 tables
Company_categories
companies
daily_rates
I want to count total volume industry wise of all the company exist within the category of companies
for example category A contains 3 companies and category B contains 5 companies so I want to sum total volume of all 3 companies in category A and so on for all categories I tried to do so but I am confused how to do with 3rd table as of I am easily been able to count the companies contains in category but not sure how to count the volume of all companies exist in a category
my table structure
company_categories
id name
+------------+----------+
| 1 | A |
|------------|----------|
| 2 | B |
|------------|----------|
companies
id name category
+------------+----------+-----------+
| 1 | co 1 | 1 |
|------------|----------|-----------|
| 2 | co 2 | 2 |
|------------|----------|-----------|
| 3 | co 3 | 1 |
|------------|----------|-----------|
daily_stock_rates
id traded_volume company_id
+------------+------------------+---------------+
| 1 | 40 | 1 |
|------------|------------------|---------------|
| 2 | 80 | 2 |
|------------|------------------|---------------|
| 3 | 30 | 3 |
|------------|------------------|---------------|
here is my code
$sql = mysqli_query($connect, "SELECT c.id category_id, c.name category_name, com.id, com.category count( dsr.total_traded_volume ) total_volume
FROM company_categories c
INNER JOIN companies com ON c.id = com.category
LEFT JOIN daily_stock_rates dsr ON com.id = dsr.company_id
GROUP BY com.category
ORDER BY total_volume DESC LIMIT 10");
while($data = mysqli_fetch_assoc($sql)) {
echo $data['category_name'] . ": .".$data['total_volume'];
echo "<br />";
}
Can Anyone help me out
joint twice to get all the rates related to the category
SELECT cat.name, SUM(rat.traded_volume) volume
FROM company_categories cat
JOIN companies comp ON comp.category = cat.id
JOIN daily_stock_rates rat ON rat.company_id = comp.id
GROUP BY cat.name
ORDER BY volume DESC
LIMIT 10
Most important diff to your query:
you need SUM(), not COUNT()
select only what you asked: volume by category. You cannot select
names of companies alongside (what company you would want to see
there anyway)
I don't think it's the join you got wrong, it's the select list and the group by clause.
First of all, if you want the total of the volume, then use sum(), not count(). Also, do not include so many fields in the select list if you want the total by category:
SELECT c.id category_id, c.name category_name, sum( dsr.total_traded_volume ) total_volume
FROM company_categories c
LEFT JOIN companies com ON c.id = com.category
LEFT JOIN daily_stock_rates dsr ON com.id = dsr.company_id
GROUP BY c.id
ORDER BY total_volume DESC LIMIT 10

Check if row has specific childs on join

I want to make a SQL to get the user which name is Mark and are the author of the posts with ids 1 and 3.
NOTE: It is unknown how many posts I need to check for. So it might need to generate that part of the SQL query using PHP.
How can that be done?
Users Table:
+----+----------+
| id | name |
+----+----------+
| 1 | Mark |
| 2 | John Doe |
+----+----------+
Posts Table
+----+-------------+-------------+
| id | text | author_id |
+----+-------------+-------------+
| 1 | First Post | 1 |
| 2 | Second Post | 2 |
| 3 | Last Post | 1 |
+----+-------------+-------------+
This is just a sample case of use, not real data.
NOTE: I know how to check if user is author on one post, but not multiple in the same row. So basicly that is what I need help with, I guess it must be a left join.
For making the check for the user named Mark and check if he is author for post id 1 I do the following:
SELECT users.*
FROM users
INNER JOIN posts
ON users.id = posts.author_id
WHERE
users.name = 'Mark'
&&
posts.author_id` = 1
I just selected the id from users. If you need more columns then just add it to the select and the group by clause.
SELECT users.id
FROM users
INNER JOIN posts ON users.id = posts.author_id
WHERE users.name = 'Mark'
AND posts.author_id in (1,3)
GROUP BY users.id
HAVING count(distinct posts.author_id) = 2
Use a sub-query to find only users with both 1 and 3:
SELECT users.*
FROM users
WHERE users.name = 'Mark'
and 2 = (select count(distinct posts.id)
where users.id = posts.author_id
and posts.id IN (1,3))
SELECT users.name, posts.posts, posts.authorid
FROM users INNER JOIN posts ON users.id = posts.authorid where posts.authorid = 1
You need to use HAVING clause to achieve desired outcome:
SELECT users.name
FROM users
INNER JOIN posts
on users.id = posts.author_id
WHERE users.name = 'Mark'
GROUP BY users.name
HAVING COUNT(posts.author_id) > 1

Comment and reply, joining 3 tables query

I have been searching now for a while, but still did not find an optimal solution. I have three tables: posts, membership and comments. I want to have a Comment and reply system.
These are my tables:
Main posts:
post_id | image_url | image_thumb_url | title | text | date | sender_id
Membership:
user_id | username | pw | email |
Comments (replies) to posts:
com_id | comment | post_id_fk | user_id_fk | ip | date
I currently have a query for retrieving the posts with the membership data:
SELECT * FROM `posts`
LEFT JOIN `membership` ON `posts`.`sender_id` = `membership`.`user_id`
UNION SELECT * FROM `posts`
RIGHT JOIN `membership` ON `posts`.`sender_id` = `membership`.`user_id`
ORDER BY `post_id` DESC LIMIT $limit $start;
I now want to add the replies to each posts, containing the membership data. Does anyone have suggestion or perhaps a better solution ?
SELECT * FROM posts AS P
LEFT JOIN comments AS C
ON P.post_id = C.post_id_fk
LEFT JOIN membership AS POST_MEMBER
ON POST_MEMBER.user_id = P.sender_id
LEFT JOIN membership AS REPLY_MEMBER
ON REPLY_MEMBER.user_id = C.user_id_fk

Show "0" in COUNT column when WHERE does not match?

What I would like to do is retrieve all data from a table, and order them by the number of games the user played in a specific category. Is there any way I can use some sort of "COUNT WHERE" sql statement?
here's what i have so far. it will only return the user if they have played a game in the "fps" category, but I want it to show all users in descending order even if they have not played an fps game. please excuse my crappy tables
SELECT user_data.user, COUNT(played_games.game_cat) as 'count'
FROM user_data, played_games
WHERE user_data.user_id = played_games.user_id and played_games.game_cat = 'fps'
GROUP BY user_data.user_id
ORDER BY 'count' DESC;
user_data table
user_id | user
1 | jeff
2 | herb
3 | dug
played_games table
id | user_id | game | game_cat
1 | 2 | kill | fps
2 | 1 | shoot| fps
3 | 2 | COD | fps
4 | 3 | dogs | cas
You need a LEFT OUTER JOIN to get the records even if a corresponding record does not exist in the other table.
SELECT user, coalesce(count(game_cat), 0) as count
FROM user_data LEFT OUTER JOIN played_games
ON user_data.user_id = played_games.user_id AND played_games.game_cat='fps'
GROUP BY user_data.user_id
ORDER BY count desc;
Gives the following result on my screen
+------+-------+
| user | count |
+------+-------+
| herb | 2 |
| jeff | 1 |
| dug | 0 |
+------+-------+
This is how I'd do it. No subquery, no COALESCE, no COUNTIF junk.
SELECT `users`.`user`, COUNT(`played_games`.id) AS `c`
FROM `users`
LEFT OUTER JOIN `played_games` ON
`users`.`user_id` = `played_games`.`user_id`
AND `played_games`.`game_cat` = "fps"
GROUP BY `users`.`user_id`
ORDER BY `c` DESC, `user` ASC
SQLFiddle (not sure if you can link them like this...)
Try this:
SELECT ud.user, coalesce(sum(pg.game_cat = 'fps'), 0) Total
FROM user_data ud
LEFT JOIN played_games pg ON ud.user_id = pg.user_id
GROUP BY ud.user_id
ORDER BY Total DESC
This will show all users and the amount of times they've played a game with category 'fps'.
The coalesce one is promising, but doesn't work for me, sigh~ I just found NULLIF is a good way to solve this problem. Remember to use LEFT JOIN
COUNT( NULLIF(TABLE.ATTR, 1) ) AS total_count
The TABLE.ATTR is some field that can be NULL, here is an example:
SELECT Posts.*, COUNT( NULLIF(Comments.user_email, 1) ) as comment_num
FROM (`Posts`)
LEFT OUTER JOIN `Comments` ON `Comments`.`post_id` = `Posts`.`id`
GROUP BY `Posts`.`id`
LIMIT 5
Got the idea from http://www.bennadel.com/blog/579-SQL-COUNT-NULLIF-Is-Totally-Awesome.htm
Below query the all game category with user id and order by count
select * from (SELECT user_data.user, COUNT(played_games.game_cat) as 'count'
FROM user_data, played_games
WHERE user_data.user_id = played_games.user_id(+) GROUP BY user_data.user_id)
order by count desc

Complicated MySQL Query

I'm creating a site in wordpress which holds information on television programs. I'm using custom fields to select each post.
The table looks something like this
+----+---------+----------+------------+
| id | post_id | meta_key | meta_value |
+----+---------+----------+------------+
| 1 | 1 | name | Smallville |
| 2 | 1 | season | 1 |
| 3 | 1 | episode | 1 |
| 4 | 2 | name | Smallville |
| 5 | 2 | season | 1 |
| 6 | 2 | episode | 2 |
+----+---------+----------+------------+
Basically what I need to do is select all of the tv shows with the name "Smallville" and sort them by season then by episodes. I thought it would be fairly simple but everything I have tried returns nothing.
Could you please explain how I can do this?
You can do something like this:
SELECT
t1.post_id,
t1.meta_value AS name,
t2.meta_value AS season,
t3.meta_value AS episode
FROM
(
SELECT *
FROM the_table
WHERE meta_key = 'name'
) t1
INNER JOIN
(
SELECT *
FROM the_table
WHERE meta_key = 'season'
) t2 ON t1.post_id = t2.post_id
INNER JOIN
(
SELECT *
FROM the_table
WHERE meta_key = 'episode'
) t3 ON t1.post_id = t3.post_id
This will give you the result:
| post_id | name | season | episode |
-------------------------------------------
| 1 | Smallville | 1 | 1 |
| 2 | Smallville | 1 | 2 |
In this form it is much easier for any operations.
What you need is to add:
WHERE name = 'Smallville'
ORDER BY season, episode
Combine the rows using a self-join, and you're good to go:
SELECT *
FROM yourtable name
INNER JOIN yourtable season
on season.post_id = name.post_id
and season.meta_key = 'season'
INNER JOIN yourtable episode
on episode.post_id = name.post_id
and episode.meta_key = 'episode'
WHERE name.meta_key = 'name'
and name.meta_value = 'Smallville'
ORDER BY season.meta_value, episode.meta_value
A more general case: sort-of conversion from your format to a more normal relational DB format:
SELECT (SELECT meta_value FROM data t1 WHERE t1.post_id = t0.post_id AND meta_key = "season") AS season,
(SELECT meta_value FROM data t1 WHERE t1.post_id = t0.post_id AND meta_key = "episode") AS episode
FROM data t0 WHERE meta_key = "name" AND meta_value = "Smallville"
For the actual sorting you can't reuse the season / episode values (those aren't assigned yet while sorting), so you have to copy/paste the subquery into the ORDER BY clause:
ORDER BY (SELECT ... "season") ASC, (SELECT ... "episode") ASC,
No need to do direct SQL.
You've got access to the SQL query through the WP_Query object. Check out the filters surrounding the where clause in the WP_Query object (there is more than 1 way to get at it) and simply modify the default WP_Query parts before they're concatenated together.
Start by setting up a WP_Query object that gets all the posts by postmeta key & postmeta value, and then tack on a bit more to the where clause to do some extra conditionals.
There's another filter that allows you to get at the ordering section of the SQL query so you can modify that.
There's no reason to hand write SQL here, just modify what has already been built for you.
the idea is to join the table to itself 3 times where for each of them take rows for a given meta_key:
SELECT t1.meta_value name, t2.meta_value season, t3.meta_value episode
FROM table t1
JOIN table t2 on t1.post_id = t2.post_id and t2.meta_key = 'season'
JOIN table t3 on t1.post_id = t3.post_id and t3.meta_key = 'episode'
WHERE t1.meta_key = 'name'
AND t1.meta_value = 'Smallville'
ORDER BY t2.meta_value, t3.meta_value

Categories