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
Related
I have three tables: Post, Community, and Community Moderator. I created an SQL query to show the communities a user moderates, including date created, and date updated. Date updated is calculated according to the MAX created date of a post in that community.
Here is the code that works for communities that have posts:
SELECT c.id, c.title, c.created, p.created as 'postdate'
FROM Post p, Community c, CommunityMod cm
WHERE p.created in
(
SELECT MAX(created)
FROM Post
GROUP BY community
)
AND cm.moderator=$id AND p.community=c.id AND cm.community=c.id
ORDER by c.created DESC
The problem is that this query will not select communities without any posts since there is no data in p.community.
I would like it to also show the communities without any posts, but instead of p.created for the updated information, it would show nothing or the date the community was created (c.created) instead.
How would I do that?
You need to use a LEFT JOIN for the Post table. Note that all conditions on columns from the Post table should be placed into the ON clause.
SELECT ...
FROM Community c
JOIN CommunityMod cm ON cm.community = c.id
LEFT JOIN Post p
ON p.community = c.id
AND p.created in
(
SELECT MAX(created)
FROM Post
GROUP BY community
)
WHERE cm.moderator = $id
ORDER by c.created DESC
Note that your subquery might not work as expected, ff Post.created is not UNIQUE. You should change it to a correlated subquery:
SELECT ...
FROM Community c
JOIN CommunityMod cm ON cm.community = c.id
LEFT JOIN Post p
ON p.community = c.id
AND p.created in
(
SELECT MAX(p1.created)
FROM Post p1
WHERE p1.community = c.id
)
WHERE cm.moderator = $id
ORDER by c.created DESC
However - If you only need the created value from the Post table, then you don't need a subquery at all.
SELECT c.id, c.title, c.created, MAX(p.created) as 'postdate'
FROM Community c
JOIN CommunityMod cm ON cm.community = c.id
LEFT JOIN Post p ON p.community = c.id
WHERE cm.moderator = $id
GROUP BY c.id
ORDER by c.created DESC
For this example I got 3 simple tables (Page, Subs and Followers):
For each page I need to know how many subs and followers it has.
My result is supposed to look like this:
I tried using the COUNT function in combination with a GROUP BY like this:
SELECT p.ID, COUNT(s.UID) AS SubCount, COUNT(f.UID) AS FollowCount
FROM page p, subs s, followers f
WHERE p.ID = s.ID AND p.ID = f.ID AND s.ID = f.ID
GROUP BY p.ID
Obviously this statement returns a wrong result.
My other attempt was using two different SELECT statements and then combining the two subresults into one table.
SELECT p.ID, COUNT(s.UID) AS SubCount FROM page p, subs s WHERE p.ID = s.ID GROUP BY p.ID
and
SELECT p.ID, COUNT(f.UID) AS FollowCount FROM page p, follow f WHERE p.ID = f.ID GROUP BY p.ID
I feel like there has to be a simpler / shorter way of doing it but I'm too unexperienced to find it.
Never use commas in the FROM clause. Always use proper, explicit, standard JOIN syntax.
Next, learn what COUNT() does. It counts the number of non-NULL values. So, your expressions are going to return the same value -- because f.UID and s.UID are never NULL (due to the JOIN conditions).
The issue is that the different dimensions are multiplying the amounts. A simple fix is to use COUNT(DISTINCT):
SELECT p.ID, COUNT(DISTINCT s.UID) AS SubCount, COUNT(DISTINCT f.UID) AS FollowCount
FROM page p JOIN
subs s
ON p.ID = s.ID JOIN
followers f
ON s.ID = f.ID
GROUP BY p.ID;
The inner joins are equivalent to the original query. You probably want left joins so you can get counts of zero:
SELECT p.ID, COUNT(DISTINCT s.UID) AS SubCount, COUNT(DISTINCT f.UID) AS FollowCount
FROM page p LEFT JOIN
subs s
ON p.ID = s.ID LEFT JOIN
followers f
ON p.ID = f.ID
GROUP BY p.ID;
Scalar subquery should work in this case.
SELECT p.id,
(SELECT Count(s_uid)
FROM subs s1
WHERE s1.s_id = p.id) AS cnt_subs,
(SELECT Count(f_uid)
FROM followers f1
WHERE f1.f_id = p.id) AS cnt_fol
FROM page p
GROUP BY p.id;
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
I have the following query:
SELECT DISTINCT (
s.styleTitle
), COUNT(p.id) AS `PictureCount`
FROM `style` s
LEFT JOIN `instagram_picture_style` ps ON s.id = ps.style_id
LEFT JOIN `instagram_shop_picture` p ON ps.picture_id = p.id
LEFT JOIN `instagram_picture_category` c ON c.picture_id = p.id
LEFT JOIN `instagram_second_level_category` sl ON c.second_level_category_id = sl.id
WHERE sl.id =25
GROUP BY p.id
ORDER BY PictureCount
however this query gives me:
I basically wanted the list to be ordered by the style that has the most pictures in it. What did I do wrong? Why is it giving me 1 on all of the styles, I am pretty sure it has more pictures for that style
ORDER BY doesn't have underscores. But equally important, you are using DISTINCT in a way where you seem to think that it is a function. It is not. It is a modifies on the SELECT and it applies to all columns.
You should group by the same column you have in the distinct. Something like this:
SELECT s.styleTitle, COUNT(p.id) AS `PictureCount`
FROM `style` s
LEFT JOIN `instagram_picture_style` ps ON s.id = ps.style_id
LEFT JOIN `instagram_shop_picture` p ON ps.picture_id = p.id
LEFT JOIN `instagram_picture_category` c ON c.picture_id = p.id
LEFT JOIN `instagram_second_level_category` sl ON c.second_level_category_id = sl.id
WHERE sl.id = 25
GROUP BY s.styleTitle
ORDER BY PictureCount DESC;
In fact, you almost never need distinct with group by. If you are using, you need to think why it would be necessary.
I have following query.
select
Product.*,
(
select
group_concat(features.feature_image order by product_features.feature_order)
from product_features
inner join features
on features.id = product_features.feature_id
where
product_features.product_id = Product.id
and product_features.feature_id in(1)
) feature_image
from products as Product
where
Product.main_product_id=1
and Product.product_category_id='1'
I want to bypass the row if feature_image is empty.
Your query looks a bit strange because you are doing most of the work in a subquery:
select p.*, (select group_concat(f.feature_image order by pf.feature_order)
from product_features pf inner join
features f
on f.id = pf.feature_id
where pf.product_id = p.id and pf.feature_id in (1)
) as feature_image
from products p
where p.main_product_id=1 and p.product_category_id='1';
A more common way to phrase the query is as an inner join in the outer query:
select p.*, group_concat(f.feature_image order by pf.feature_order) as feature_image
from products p join
product_features pf
on pf.product_id = p.id and pf.feature_id in (1) join
features f
on f.id = pf.feature_id
where p.main_product_id=1 and p.product_category_id='1'
group by p.id;
This will automatically include only products that have matching features. You would use left outer join to get all products.