This my query
Select articles.id,articles.userid,articles.article,count(articles_likes.id), count(article_dislikes.id)
from articles
Left join article_likes
on article_likes.article_id=articles.id
Left join article_dislikes
on article_dislikes.article_id=articles.id
group by articles.id ;
I want to count the number of rows in article_likes table and article_dislikes table im getting the value correct upto 2 rows. When there is a third and so on entries. I'm getting wrong counting of rows.
I don't know where the problem is. I think I may be getting wrong values because I'm using the same table for two times.
I have three tables
1)articles contains id,userid and article
2)Articles_likes table contains-like_id ,user_id and article_id
3)Articles_dislikes table contains dislike_id ,user_id and article_id
Your joins are combining every like of an article with every dislike of the same article. As a result you get the product of the two counts. So if an article has 3 likes and 2 dislikes, the joins will generate 6 rows and you get 6 as result for both counts. You could workaround with COUNT(DISTINCT ..), but if you have alot of likes and dislikes, you might also get performance issues. So you should avoid these joins and either preaggregate in subqueries in FROM clause, or use correlated subqueries in SELECT clause like here:
select
articles.id,
articles.userid,
articles.article,
(
select count(*)
from article_likes
where article_likes.article_id = articles.id
) as likes_count,
(
select count(*)
from article_dislikes
where article_dislikes.article_id = articles.id
) as dislikes_count
from articles
Related
I know there's many questions/answers for slow queries, but I'm struggling to relate an existing answer to my example.
I have the following simple query which counts article views in a subquery:
SELECT
articles.id,
articles.views,
articles.title,
articles.slug,
articles.created_at,
(SELECT count(*) FROM tracking WHERE element_id = articles.id AND tracking_type = 'article_view') AS tracking_views
FROM articles
WHERE articles.company_id = 123
ORDER BY articles.created_at DESC
This particular company has ~250 articles, and the query takes over 12 seconds.
Is there a better/more efficient way I could be doing this?
Try joining to a group by. Its pretty hard to say without knowing how many articles / views and companies there are though.
What you want is for SQL to be able to to the aggregation of tracking in one go, rather than individually for every row in the result, which is implied by the position of your tracking_view sub select.
If your lucky (I didnt check) the join to the counts sub select will be smart enough to skip any articles that are not for the right company. If not you can include the join back to company in the counts sub select.
eg
select a.*, counts.count
from articles a
join (
select count(*) as count, element_id
from tracking
where tracking_type = 'article_view'
group by tracking.element_id
) as counts on counts.element_id = a.id
where a.company_id = 123
ORDER BY articles.created_at DESC
I am doing a sub-query join to another table as I wanted to be able to sort the results I got back with it, I only need the first row but I need them ordered in a certain way so I would get the lowest id.
I tried adding LIMIT 1 to this but then the full query returned 0 results; so now it has no limit and in the EXPLAIN I have two rows showing they are using the full 10k+ rows of the auction_media table.
I wrote it this way to avoid having to query the auction_media table for each row separately, but now I'm thinking that this way isn't that great if it has to use the whole auction_media table?
Which way is better? The way I have it or querying the auction_media table separately? ...or is there a better way!?
Here is the code:
SELECT
a.auction_id,
a.name,
media.media_url
FROM
auctions AS a
LEFT JOIN users AS u ON u.user_id=a.owner_id
INNER JOIN ( SELECT media_id,media_url,auction_id
FROM auction_media
WHERE media_type=1
AND upload_in_progress=0
ORDER BY media_id ASC
) AS media
ON a.auction_id=media.auction_id
WHERE a.hpfeat=1
AND a.active=1
AND a.approved=1
AND a.closed=0
AND a.creation_in_progress=0
AND a.deleted=0
AND (a.list_in='auction' OR u.shop_active='1')
GROUP BY a.auction_id;
Edit: Through my testing, using the above query seems like it would be the much faster method overall; however I worry if that will still be the case when the auction_media table grows to like 1M rows or something.
edit: As stated in the comments - DISTINCT is not required because the auctions table can only be associated with (at most) one user table row and one row in the inner query.
You may want to try this. The outer query's GROUP BY is replaced with DISTINCT since you don't have any aggregate function. The inner query, was replaced by a query to find the smallest media_id per auction_id, then JOINed back to get the media_url. (Since I didn't know if the media_id and auction_id were a composite unique key, I used the same WHERE clause to help eliminate potential duplicates.)
SELECT
a.auction_id,
a.name,
auction_media.media_url
FROM auctions AS a
LEFT JOIN users AS u
ON u.user_id=a.owner_id
INNER JOIN (SELECT auction_id, MIN(media_id) AS media_id
FROM auction_media
WHERE media_type=1
AND upload_in_progress=0
GROUP BY auction_id) AS media
ON a.auction_id=media.auction_id
INNER JOIN auction_media
ON auction_media.media_id = media.media_id
AND auction_media.auction_id = media.auction_id
AND auction_media.media_type=1
AND auction_media.upload_in_progress=0
WHERE a.hpfeat=1
AND a.active=1
AND a.approved=1
AND a.closed=0
AND a.creation_in_progress=0
AND a.deleted=0
AND (a.list_in='auction' OR u.shop_active='1');
I have to tables in my database, the first one (participants) look just like that:
And I have another called votes in which I can vote for any participants.
So my problem is that I'm trying to get all the votes of each participant but when I execute my query it only retrieves four rows sorted by the COUNT of votes, And the other remaining are not appearing in my query:
SELECT COUNT(DISTINCT `votes`.`id`) AS count_id, participants.name
AS participant_name FROM `participants` LEFT OUTER JOIN `votes` ON
`votes`.`participant_id` = `participants`.`id` GROUP BY votes.participant_id ORDER BY
votes.participant_id DESC;
Retrieves:
I think the problem is that you're grouping by votes.participant_id, rather than participants.id, which limits you to participants with votes, the outer join notwithstanding. Check out http://sqlfiddle.com/#!2/c5d3d/5/0
As what i have understood from the query you gave you were selecting unique id's from the votes table and I assume that your column id is not an identity. but it would be better if that would be an identity? and if so, here is my answer.replace your select with these.
Select count (votes.participant.id) as count_id ,participants.name as participant_name
from participants join votes
on participants.id = vote.participant_id
group by participants.name
order by count_id
just let me know if it works
cheers
My database has news articles and blog posts. The primary key for both is an ItemID that is unique across both tables.
The articles are in a table that has the following fields
item_id
title
body
date_posted
The blogposts table has the following fields
item_id
title
body
date_posted
both tables have extra fields unique to them.
I have a third table that holds meta information about articles and posts.
The items table has the following fields
item_id
source_id
...
every blogpost and article has a record in the items table and a record in its respective table.
What I am trying to do is build a query that will count the number of items posted per day. I can do it for one table using a count grouped by date_posted but how to combine articles and posts count in one query?
Similar to Dems, but slightly simpler:
select date_posted, count(*)
from (select date_posted from article union all
select date_posted from blogposts) v
group by date_posted
You can do it two ways.
1. Join everything together and then aggregate (See Tom H's answer).
2. Aggregate each table, UNION them, and aggregate again.
Option 1 may seem shorter, but will mean that you may not benefit from INDEXes on the root tables (As they have to be re-ordered for the JOIN). So I'll show option 2, which is the direction you were headed any way.
SELECT
date_posted,
SUM(daily_count) AS daily_count
FROM
(
SELECT date_posted, COUNT(*) AS daily_count FROM article GROUP BY date_posted
UNION ALL
SELECT date_posted, COUNT(*) AS daily_count FROM blogposts GROUP BY date_posted
)
AS combined
GROUP BY
date_posted
This should be fastest, provided that you have an index on each table where date_posted is the first field in the index. Other-wise the tables will still need to be re-ordered for the aggregation.
I would have used a different table design for this, with types and subtypes. Your Items table has a single column primary key and your Blog_Posts and Articles tables' primary keys are the same ID with a foreign key to the Items table. That would make something like this pretty easy to do and also helps to ensure data integrity.
With your existing design, your best bet is probably something like this:
SELECT
I.item_id,
I.source_id,
COALESCE(A.date_posted, B.date_posted) AS date_posted,
COUNT(*) AS date_count
FROM
Items I
LEFT OUTER JOIN Articles A ON
A.item_id = I.item_id AND
I.source_id = 'A' -- Or whatever the Articles ID is
LEFT OUTER JOIN Blog_Posts B ON
B.item_id = I.item_id AND
I.source_id = 'B' -- Or whatever the Blog_Posts ID is
GROUP BY
I.item_id,
I.source_id,
COALESCE(A.date_posted, B.date_posted)
You could also try using a UNION:
SELECT
SQ.item_id,
SQ.source_id,
SQ.date_posted,
COUNT(*) AS date_count
FROM
(
SELECT I1.item_id, I1.source_id, A.date_posted
FROM Items I1
INNER JOIN Articles A ON A.item_id = I1.item_id
WHERE I1.source_id = 'A'
UNION ALL
SELECT I2.item_id, I2.source_id, B.date_posted
FROM Items I2
INNER JOIN Articles B ON B.item_id = I2.item_id
WHERE I2.source_id = 'B'
)
select item_id, date_posted from blogposts where /* some conditions */
union all select item_id, date_posted from articles where /* some conditions */
You'll probably need to put that into a subquery, and if you so desire, join it with other tables, when running the group by. But the main point is that union is the operator you use to combine like data from different tables. union all tells the database that you don't need it to combine duplicate records, since you know that the two tables will never share an item_id, so it's a little faster (probably).
I have three tables that I need get information from, 1 table has the information in and the other two hold information that i need to count.
so the first tables structure is:
tbl_img
img_id
img_name
tbl_comments
comment_id
img_id
comment
tbl_vote
vote_id
logo_id
I want the results to have the count of comments and votes that relate to each logo.
I have a bit of the query which is for the count of comments, but have no idea for the syntax for the second join.
SELECT l.img_id, l.img_name, COUNT(c.comment_id) AS comment_count
FROM tbl_images as l
LEFT OUTER JOIN tbl_comments AS c USING (img_id);
Can anyone help?
how about this :
SELECT l.img_id, l.img_name,
(SELECT COUNT(*) FROM tbl_comments c WHERE i.img_id = c.img_id ) AS comment_count,
(SELECT COUNT(*) FROM tbl_vote v WHERE i.img_id = v.img_id ) AS vote_count
FROM tbl_images i
Sounds like you need two queries for this: One for counting the votes, and one for counting the comments.
As far as I know, COUNT counts result rows, and joins create result rows to display all allowed permutations of joined tables.
Assuming you have I entries, each with J comments and K votes, you would receive J*K rows for each entry after joins, and COUNTs would both return that J*K instead of the correct amount.
I do not remember if you can do inner queries in MySQL, but that would be the way to go.
(See #Kevin Burtons answer)