Joining two select queries and ordering results - mysql

Basically I'm just unsure as to why this query is failing to execute:
(SELECT replies.reply_post, replies.reply_content, replies.reply_date AS d, members.username
FROM (replies) AS a
INNER JOIN members ON replies.reply_by = members.id)
UNION
(SELECT posts.post_id, posts.post_title, posts.post_date AS d, members.username
FROM (posts) as b
WHERE posts.post_set = 0
INNER JOIN members ON posts.post_by = members.id)
ORDER BY d DESC LIMIT 5
I'm getting this error:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use
near 'a INNER JOIN members ON replies.re' at line 2
All I'm trying to do is select the 5 most recent rows (dates) from these two tables. I've tried Join, union etc and I've seen numerous queries where people have put another query after the FROM statement and that just makes no logical sense to me as to how that works?
Am I safe to say that you can join the same table from two different but joined queries? Or am I taking completely the wrong approach, because frankly I can't seem see how this query is failing despite reading the error message.
(The two queries on there own work fine)

I think there is syntax error in your query at below part :
FROM (posts) as b
WHERE posts.post_set = 0
INNER JOIN members ON posts.post_by = members.id)
Inner join should come first before where condition. Also your join conditions are wrong. You need to apply conditions like
INNER JOIN members ON a.reply_by = members.id)
INNER JOIN members ON b.post_by = members.id)
So your query should be like this
(SELECT a.reply_post, a.reply_content, a.reply_date AS d, members.username
FROM (replies) AS a
INNER JOIN members ON a.reply_by = members.id)
UNION
(SELECT b.post_id, b.post_title, b.post_date AS d, members.username
FROM (posts) as b
INNER JOIN members ON b.post_by = members.id
WHERE b.post_set = 0)
ORDER BY d DESC LIMIT 5

Try this:
(SELECT a.reply_post, a.reply_content, a.reply_date AS d, members.username
FROM replies AS a
INNER JOIN members ON a.reply_by = members.id)
UNION
(SELECT b.post_id, b.post_title, b.post_date AS d, members.username
FROM posts as b
INNER JOIN members ON b.post_by = members.id
WHERE b.post_set = 0) /* Use where condition after matching Id's using ON */
ORDER BY d DESC LIMIT 5

Related

Mysql : Get participants list of each conversation

I have 2 tables conversation and participants, I would like to get the list of conversations and participants in each of them. Can I do that in only one query or I have to do 2 queries one for conversation and the second for getting participants for each conversation ?
I tried with
SELECT c.*, (SELECT p.user FROM participants p WHERE p.conversation_id = c.id ) AS participants
FROM `conversation` c
ORDER BY c.date DESC
But i get "error 1242 subquery returns more than 1 rows" and that's normal !
Use an INNER JOIN to select parts of different tables where a common ID is shared. Like this:
SELECT c.*, p.user
FROM conversation AS c INNER JOIN participants AS p ON p.conversation_id = c.id
ORDER BY c.date DESC
Right now you are using a subquery
(SELECT p.user FROM participants p WHERE p.conversation_id = c.id )
to receive a new column, in the table you are creating. A column only has one value for every row, not multiple values. So an error is thrown in this case. If you are confident that you will not miss data then you could force your subquery to return one each time it is run with aggregates
(SELECT max(p.user) FROM participants p WHERE p.conversation_id = c.id )
But if the multiple values are different and still important, which in most cases is likely you want to do the join as mentioned by my friend Erik.
A join is likely what you are looking for.
SELECT c.*, p.user
FROM conversation c
inner join
Participants p
on p.conversation_id = c.id
ORDER BY c.date DESC

Deciding between two methods to check if a row exists (more subqueries VS more left joins)

I want to fetch one product information using joins from different tables. I have three additional tables (review,thread,award) and I'd like to check whether records exist relating to this specific product. If they exist, return a non-null value, otherwise null. There is a possibility that more of this type of checking will be added to the query in the future.
Which query would you prefer performance wise to test if records exists?
Using exists with multiple subqueries:
$sql = "SELECT p.product_id,p.name,m.model,m.model_id,b.brand,me.merchant,
EXISTS(SELECT 1 FROM review WHERE product_id = :id) AS has_review,
EXISTS(SELECT 1 FROM thread WHERE product_id = :id) AS has_thread,
EXISTS(SELECT 1 FROM award WHERE product_id = :id) AS has_award
FROM product p
INNER JOIN model m ON m.model_id = p.model_id
INNER JOIN brand b ON b.brand_id = m.brand_id
INNER JOIN merchant me ON me.merchant_id = m.merchant_id
WHERE p.product_id = :id
LIMIT 1";
$dbh->prepare($sql);
Using multiple left joins:
$sql = "SELECT p.product_id,p.name,m.model,m.model_id,b.brand,me.merchant,
(t.product_id is not null) AS has_thread,
(r.product_id is not null) AS has_review,
(a.product_id is not null) AS has_award
FROM product p
INNER JOIN model m ON m.model_id = p.model_id
INNER JOIN brand b ON b.brand_id = m.brand_id
INNER JOIN merchant me ON me.merchant_id = m.merchant_id
LEFT JOIN review r ON re.product_id = p.product_id
LEFT JOIN thread t ON t.product_id = p.product_id
LEFT JOIN award a ON a.product_id = a.product_id
WHERE p.product_id = :id
LIMIT 1";
The first is much preferable.
For performance, for either version, you want indexes on review(product_id), thread(product_id), and award(product_id).
Why is using EXISTS better? When no matching rows exist in the three tables, then the two versions should be equivalent (minus the typo in the last on clause on the second query). However, when rows do exist, then the second version will create cartesian products of those rows, throwing off both the results and performance.
Note: I would be inclined to write the EXISTS clause using correlated subqueries, so the parameter is only referenced once:
EXISTS (SELECT 1 FROM review r WHERE r.product_id = p.product_id) AS has_review,
EXISTS (SELECT 1 FROM thread t WHERE t.product_id = p.product_id) AS has_thread,
EXISTS (SELECT 1 FROM award a WHERE a.product_id = p.product_id) AS has_award,

How can I use OR in a left join in MySQL?

Maybe there's a better way to do this... I have a table of member friend requests. The columns are request_id, author_id, recipient_id, status(accepted or denied). I also have a members table whose id is linked to the author or recipient. I want to get a list of a member's friends by selecting from the members table and then joining the requests table. Since the member can be either the author or the recipient of any, some, or none of the requests, a simple LEFT JOIN member_requests AS r ON member_id = r.author_id wouldn't work. How can I write a query that will do this?
SELECT
m.member_id, m.display_name
r.author_id, r.recipient_id, r.status
FROM members AS m
LEFT JOIN member_requests AS r ON m.member_id = r.recipient_id
WHERE r.status = 1 --Accepted
ORDER BY m.display_name
You can use an OR in your left join, like so:
LEFT JOIN member_requests AS r
ON m.member_id = r.recipient_id
OR m.member_id = r.author_id
However, your where clause also needs to be altered:
SELECT
m.member_id, m.display_name
r.author_id, r.recipient_id, r.status
FROM members AS m
LEFT JOIN member_requests AS r
ON (m.member_id = r.recipient_id
OR m.member_id = r.author_id)
AND r.status = 1 //Accepted
ORDER BY m.display_name
When you left join table A to table B, and then specify a restriction in your where clause on table B, you convert your left join into an inner join. Typically, a left join to table B would yield some null values, since table A might have records that don't join to table B. But if you say 'where table B.value = x', you restrict your join to only rows in which table A joins to table B, and furthermore to rows in which 'B.value = x'. The join is then evaluated as an inner join, rather than a left outer.

How do I write a My(SQL) query that counts from multiple tables based on specific WHERE clause criteria

I have 5 tables: a, b, c, d and e.
Each table is joined by an INNER JOIN on the id field.
My query is working perfectly fine as it is but I need to enhance it to count the result so I can echo it to the screen. I have not been able to get the count working.
There are very specific fields I am querying:
state_nm
status
loc_type
These are all parameters I enter manually into the query like so:
$_POST["state_nm"] = 'AZ'; ... // and for all other below values..
SELECT *
FROM main_table AS a
INNER JOIN table_1 AS b ON a.id = b.id
INNER JOIN table_2 AS c ON b.id = c.id
INNER JOIN blm table_3 AS d ON c.id = d.id
INNER JOIN table_4 AS e ON d.id = e.id
WHERE a.trq != ''
AND b.state_nm = '".$_POST["state_nm"]."'
AND b.loc_type LIKE \ "%".$_ POST ["loc_type"]."%\"
AND b.STATUS = '".$_POST["status"]."'
GROUP BY b.NAME
ORDER BY c.county ASC;
not sure I get exactly what is your goal here.
anyway, using "select *" and group by in the same query is not recommended and in some databases will raise an error
what I would do is something like that:
select a.name, count(*) from (
SELECT * FROM main_table as a
INNER JOIN table_1 as b
ON a.id=b.id
INNER JOIN table_2 as c
ON b.id=c.id
INNER JOIN blm table_3 as d
ON c.id=d.id
INNER JOIN table_4 as e
ON d.id=e.id
WHERE a.trq != ''
AND b.state_nm = '".$_POST["state_nm"]."'
AND b.loc_type LIKE \"%".$_POST["loc_type"]."%\"
AND b.status = '".$_POST["status"]."'
)a
group by a.name
the basic idea is to add an outer query and use group by on it...
hopefully this solves your problem.
In place of
SELECT *
in your query, you could replace that with
SELECT COUNT(*)
That query should return the number of rows that would be in the resultset for the query using SELECT *. Pretty easy to test, and compare the results.
I think that answers the question you asked. If not, I didn't understand your question.
I didn't notice the GROUP BY in your query.
If you want to get a count of rows returned by that query, wrap it in outer query.
SELECT COUNT(1) FROM (
/* your query here */
) c
That will give you a count of rows returned by your query.

MySQL Subquery Question

I have a query to pull a total number for a given publisher ID. I'd like to use it as a subquery so I can iterate over all publisher IDs.
My working query for a given ID is:
SELECT SUM( d.our_cost )
FROM articles a
CROSS JOIN domains d ON a.domain_id = d.id
AND d.publisher_id = '1094'
I'd like to pull this figure for all ID's in publisher p table where d.publisher_id = p.id
So far I've tried the following to no avail:
SELECT p.id, p.contact_name, p.contact_email,
(SELECT SUM(d.our_cost)
FROM articles a
CROSS JOIN domains d ON a.domain_id = d.id and d.publisher_id = p.id) total
FROM publishers p
The specific error I'm getting is: Unknown column 'p.id' in 'on clause'
I think you should modify your query and put the subquery in the from clause, something like this:
SELECT p.id, p.contact_name, p.contact_email, total.total_cost
FROM
(
SELECT SUM(d.our_cost) as total_cost, d.publisher_id
FROM articles a CROSS JOIN domains d ON a.domain_id = d.id ) total
JOIN publishers p on total.publisher_id = p.id
I'm assuming you've gotten an error about your syntax, try:
SELECT p.id, p.contact_name, p.contact_email, SUM(d.our_cost) as total
FROM articles a
CROSS JOIN domains d ON a.domain_id = d.id
JOIN publishers p ON d.publisher_id = p.id
seems like a group by would be handy here instead
Also it seems like you dont need articles table at all (unless you have additional business rules)
SELECT p.id, p.contact_name, p.contact_email, IFNULL(SUM(d.our_cost),0) AS total
FROM publishers p
LEFT JOIN domains d ON d.publisher_id = p.id
GROUP BY p.id