Table Article id title
Table Comment id articleID comment
SELECT a.*, count(c.id) FROM Article as a LEFT JOINT Comment as c
ON c.articleID = a.id
LIMIT 0, 10
I want to display article with number comments, but it only list 1 result (has comment).
and do not list the articles do not have comments.
How to list all articles (have comments/ have not comment) ?
First of all you have to use GROUP BY in your base query
SELECT a.id, a.title, COUNT(c.id) comment_count
FROM Article a LEFT JOIN Comment c
ON c.articleID = a.id
GROUP BY a.id, a.title
Sample output:
| ID | TITLE | COMMENT_COUNT |
-------------------------------
| 1 | Title1 | 2 |
| 2 | Title2 | 0 |
Here is SQLFiddle demo
Now if you are using LEFT JOIN and want only articles with comments you need to apply HAVING clause
SELECT a.id, a.title, COUNT(c.id) comment_count
FROM Article a LEFT JOIN Comment c
ON c.articleID = a.id
GROUP BY a.id, a.title
HAVING comment_count > 0
or simply use INNER JOIN as Andy suggested because inner join will filter out all mismatches (meaning all articles that have no corresponding records in comments table and vice versa). Using INNER JOIN in most cases is faster then using LEFT JOIN.
SELECT a.id, a.title, COUNT(c.id) comment_count
FROM Article a JOIN Comment c
ON c.articleID = a.id
GROUP BY a.id, a.title
Both will produce:
| ID | TITLE | COMMENT_COUNT |
-------------------------------
| 1 | Title1 | 2 |
Here is SQLFiddle demo
Do not LEFT JOIN...
When you left join, you simply join one matching row to table Article, you want to simply join the tables as you are then grouping by the article id and counting the grouped results
SELECT a.*, count(a.id) FROM Article as a, Comment as c
WHERE c.articleID = a.id
GROUP BY a.id
LIMIT 0, 10
Related
My question is about how to optimize and reduce size of a sql query. I want to join more than 20 multiple queries using UNION, it is giving me the correct result as per the below logic, but I am looking for two things here
something more efficient
I already have 20 UNIONS in my query, and every month I have to add 2-4 UNIONS more which make this query very long so is there any way this query can be rephrased with less code
Select
'343' As 'Manual ID',
'24/07/2020' As 'Date',
A.ID,
O.Order_Name,
C.Customer_Name,
Q.Quantity
From Shipper A
Left Join Order O A.ID = O.ID
Left Join Customer C A.ID = C.ID
Left Join Quantity Q Q.ID = C.ID
where A.ID IN (1)
UNION
Select
'323' As 'Manual ID',
'24/08/2020' As 'Date',
A.ID,
O.Order_Name,
C.Customer_Name,
Q.Quantity
From Shipper A
Left Join Order O A.ID = O.ID
Left Join Customer C A.ID = C.ID
Left Join Quantity Q Q.ID = C.ID
where A.ID IN(2,3,4)
and so on ...
Result
Manual ID | Date | Shipper | Order Name | Customer Name | Qty
343 | 24/07/2020 | 1 | order1 | A | 5
323 | 24/08/2020 | 2 | order2 | B | 2
323 | 24/08/2020 | 3 | order3 | C | 1
323 | 24/08/2020 | 4 | order4 | D | 12
You can try this:
Select
CASE
WHEN A.ID IN(1) THEN '343'
WHEN A.ID IN(2,3,4) THEN '323'
END As 'Manual ID',
CASE
WHEN A.ID IN(1) THEN '24/07/2020'
WHEN A.ID IN(2,3,4) THEN '24/08/2020'
END As 'Date',
A.ID,
O.Order_Name,
C.Customer_Name,
Q.Quantity
From Shipper A
Left Join Order O A.ID = O.ID
Left Join Customer C A.ID = C.ID
Left Join Quantity Q Q.ID = C.ID
Where A.ID IN(1,2,3,4)
First suggestion is to move the parameters in to another table, then join on it. You can even make that an inline view if you don't want to use a real table...
Second suggestion is to use UNION ALL to avoid the costs of deduplication incurred by UNION.
SELECT
params.*,
O.Order_Name,
C.Customer_Name,
Q.Quantity
FROM
(
SELECT '343' As 'Manual ID', '24/07/2020' As 'Date', 1 AS A_ID
UNION ALL SELECT '323' As 'Manual ID', '24/08/2020' As 'Date', 2 AS A_ID
UNION ALL SELECT '323' As 'Manual ID', '24/08/2020' As 'Date', 3 AS A_ID
UNION ALL SELECT '323' As 'Manual ID', '24/08/2020' As 'Date', 4 AS A_ID
)
AS params
INNER JOIN Shipper A ON A.ID = params.A_ID
Left Join Order O ON A.ID = O.ID
Left Join Customer C ON C A.ID = C.ID
Left Join Quantity Q ON Q.ID = C.ID
Alternatively, don't recompute this every month. Write a new query each month, and insert the results into another table?
If you just want to go for query the better way would be to use the case when statement but every now and then you need to keep updating the query adding new cases.
Another, optimized solution will be to create a new table to store
Manual ID, Date, (Common) ID present in Shipper (Table). Then create a view to join all above tables with new Table.
New Table
Manual ID | Date | ID |
343 | 24/07/2020 | 1 |
323 | 24/08/2020 | 2 |
323 | 24/08/2020 | 3 |
323 | 24/08/2020 | 4 |
Then Create a View Joining all Tables including new new table with ID.
In this you just need add new value to new table and you will complete result in view it self.
CREATE VIEW MY_VIEW
AS
SELECT * FROM
(
Select
T.[Manual ID],
T.[Date],
A.ID,
O.Order_Name,
C.Customer_Name,
Q.Quantity
From Shipper A
Left Join Order O A.ID = O.ID
Left Join Customer C A.ID = C.ID
Left Join Quantity Q Q.ID = C.ID
Left Join NewTable T T.ID = A.ID
)
Now just insert value in new table and fetch complete data from MY_VIEW. It will give the same result as you are excepting.
This is the query:
SELECT a.id, a.userName,if(o.userId=1,'C',if(i.userId=1,'I','N')) AS relation
FROM tbl_users AS a
LEFT JOIN tbl_contacts AS o ON a.id = o.contactId
LEFT JOIN tbl_invites AS i ON a.id = i.invitedId
ORDER BY relation
This returns the output as follows:
+----+--------------+-------------+
| ID | USERNAME | RELATION |
+----+--------------+-------------+
| 1 | ray | C |
+----+--------------+-------------+
| 2 | john | I |
+----+--------------+-------------+
| 1 | ray | N |
+----+--------------+-------------+
I need to remove the third row from the select query by checking if possible that id is duplicate. The priority is as follows:
C -> I -> N. So since there is already a "ray" with a C, I dont want it again with an I or N.
I tried adding distinct(a.id) but it doesn't work. How do I do this?
Why doesn't DISTINCT work for this?
From the specs you gave, all you have to do is group by ID and username, then pick the lowest value of relation you can find (since C < I < N)
SELECT a.id, a.userName, MIN(if(o.userId=1,'C',if(i.userId=1,'I','N'))) AS relation
FROM tbl_users AS a
LEFT JOIN tbl_contacts AS o ON a.id = o.contactId
LEFT JOIN tbl_invites AS i ON a.id = i.invitedId
GROUP BY a.id, a.username
There are multiple ways to get the group-wise maximum/minimum as you can see in this manual page.
The best one suited for you is the first one, if the order of the rows can not be defined by alphabetic order.
In this case, given if the desired order were z-a-m (see Rams' comment) you'd need the FIELD() function.
So your answer is
SELECT
a.id,
a.userName,
if(o.userId=1,'C',if(i.userId=1,'I','N')) AS relation
FROM tbl_users a
LEFT JOIN tbl_contacts AS o ON a.id = o.contactId
LEFT JOIN tbl_invites AS i ON a.id = i.invitedId
WHERE
if(o.userId=1,'C',if(i.userId=1,'I','N')) = (
SELECT
if(o.userId=1,'C',if(i.userId=1,'I','N')) AS relation
FROM tbl_users aa
LEFT JOIN tbl_contacts AS o ON aa.id = o.contactId
LEFT JOIN tbl_invites AS i ON aa.id = i.invitedId
WHERE aa.id = a.id AND aa.userName = a.userName
ORDER BY FIELD(relation, 'N', 'I', 'C') DESC
LIMIT 1
)
Note, you can also do it like ORDER BY FIELD(relation, 'C', 'I', 'N') to have it more readable / intuitive. I turned it the other way round, because if you'd have the possibility of having a 'X' in the relation, the FIELD() function would have returned 0 because X is not specified as a parameter. Therefore it would be sorted before 'C'. By sorting descending and turning the order of the parameters around this can not happen.
My database
category_group(id,name)
category(id,name,cat_group_id)
topic(id,name,cat_id)
comment(id,name,topic_id)
I want to get:
Category Group 1
=====================
Category 1
Count topic | Count comment
-------------------
Category 2
Count topic | Count comment
Category Group 2
=====================
Category 3
Count topic | Count comment
-------------------
Category 4
Count topic | Count comment
I can only do with a lot of different query but I think it is not good practice.
If all the table are strictly related you can use inner join
select a.*, b.*, c.*, d.*
from category as a
inner join category_group as b on a.cat_group_id = b id
inner join topic as c on a.id = c.cat_id
inner join comment as d.topic_id = c.id
else where need use left join
Then for in your case you can do this
select b.name, a.name, count(d.*) as count_commect, count(c.*) as count_topic
from category as a
inner join category_group as b on a.cat_group_id = b id
inner join topic as c on a.id = c.cat_id
inner join comment as d.topic_id = c.id
group by a.name, b.name
order by a.name, b.name
I don't know if this question is apropriate on this forum.
I have a huge query :
SELECT threshold.id, brand.id, COUNT(brand.id), threshold
FROM current_stock, article, product, brand, delivery, threshold
WHERE current_stock.article_id = article.id
AND article.product_product_code = product.product_code
AND product.brand_id = brand.id
AND article.delivery_id = delivery.id
AND delivery.store_id = 'E260'
AND threshold.brand_id = brand.id
GROUP BY brand.id
HAVING COUNT(brand.id) <= threshold
UNION
SELECT threshold.id, brand.id, 0, threshold
FROM current_stock, article, product, brand, delivery, threshold
WHERE threshold.store_id = 'E260'
AND threshold.brand_id NOT IN (
SELECT brand_id FROM current_stock, article, product, delivery
WHERE current_stock.article_id = article.id
AND article.product_product_code = product.product_code
AND article.delivery_id = delivery.id
AND delivery.store_id = 'E260')
And I think it's possible to do better but after a entire day of try I haven't found a better query giving the same result.
For clarify, I have a stock (with current_stock, article, product and delivery). I also have thresholds. what I want is to check for each thresholds if there is the given minimum amount of stock for the given brand.
My problem is that if there is 0 article of a brand, the first part of the query will not take care about the threshold on this brand. It's why I have added an uggly Union.
Someone have an idea for a better way to do this ?
EDIT
This what I have done after the reading of comments and answers :
SELECT t.id, b.id, t.threshold, count(b.id) stock
FROM threshold t
inner join brand b on b.id = t.brand_id
left join product p on p.brand_id = b.id
inner join article a on a.product_product_code = p.product_code
inner join delivery d on d.id = a.delivery_id
inner join current_stock cs on cs.article_id = a.id
WHERE
t.store_id = 'E260' AND
d.store_id = 'E260'
GROUP BY b.id
HAVING stock <= t.threshold
My problem is that it don't gives all threholds... only ones that have at least one 'current_stock'. I have perhaps don't understand how joins are working.
Here an example of threshold table :
| id | brand_id | threshold |
-----------------------------
| 1 | 86 | 1 |
| 2 | 28 | 1 |
| 3 | 12 | 1 |
What I want as result this :
# with 2 entries in 'current_stock' for the brand id 28, 1 for 12 and 0 for 86
| t.id | b.id | threshold | stock |
-----------------------------------
| 1 | 86 | 1 | 0 |
| 3 | 12 | 1 | 1 |
Guessing a few parts here since you've used implicit joins in your sample. An explicit version would look something like this (provided I guessed correctly for how you are joining the threshold table).
SELECT
t.id,
b.id,
COUNT(b.id),
t.threshold
FROM
current_stock c
inner join article a on a.id = c.article_id
inner join delivery d on d.id = a.delivery_id
inner join product p on p.product_code = a.product_product_code
inner join brand b on b.id = p.brand_id
inner join threshold t on t.brand_id = b.id
WHERE
d.store_id = 'E260'
GROUP BY b.id
HAVING COUNT(b.id) <= t.threshold
Now to get your results to include rows where there aren't any 'articles' you can start switching out the inner joins for left joins. However, you can't simply use left outer join article... in the example above, because the store_id in the WHERE clause will just turn it back into a pseudo inner join.
Instead, is there a different field you can join the delivery table on from current_stock?
EDIT - 07/29/15
I think you're close, you may just have one too many filters and you're counting from 'b' when the wanted outcome suggests you should be counting from 'cs' instead. Try this:
SELECT t.id, b.id, t.threshold, count(cs.id) stock
FROM
threshold t
inner join brand b on b.id = t.brand_id
inner join product p on p.brand_id = b.id
inner join article a on a.product_product_code = p.product_code
left outer join delivery d on d.store_id = t.store_id
left outer join current_stock cs on cs.article_id = a.id
WHERE
t.store_id = 'E260'
GROUP BY b.id
HAVING stock <= t.threshold
I am trying to achieve total number of topics, total number of posts, and last post for given section, please find db structures and query as following...
fcats
| id | title | section |
+----+--------+---------+
| 1 | test | gd |
+----+--------+---------+
ftopics
| id | title | cat_id |
+----+--------+---------+
| 1 | test1 | 1 |
+----+--------+---------+
fposts
| id | post | topic_id | posted_by
+----+-------+----------+---------+
| 1 | post | 1 | user |
+----+-------+----------+---------+
current query
SELECT id,
title ,
(SELECT count(id)
FROM ftopics
WHERE cat_id = id) AS total_topics
FROM fcats
WHERE section = "gd"
by using above query, i could only get total_topics for given section, but i am confused about how to get total number of posts, and last post for given section. please help, thanks.
SELECT A.id section_id,
IFNULL(COUNT(DISTINCT B.id),0) topics_count,
IFNULL(COUNT(C.id),0) post_count,
(SELECT post from fposts where id = MAX(C.id)) last_post
FROM fsections A LEFT JOIN ftopics B
ON A.id = B.cat_id
LEFT JOIN fposts C
ON C.topic_id = B.id
WHERE A.section = "gd"
GROUP BY A.id
Also include the null case if the section doesnot have any post
Maybe something like this:
SELECT
id,
title ,
(
SELECT
count(ftopics.id)
FROM
ftopics
WHERE
ftopics.cat_id = fcats.id
) AS total_topics,
(
SELECT
COUNT(distinct fposts.id)
FROM
ftopics
JOIN fposts
ON ftopics.id=fposts.topic_id
WHERE
ftopics.cat_id = fcats.id
),
(
select
fposts.id
from fposts
inner join ftopics on fposts.topic_id = ftopics.id
inner join fcats c2 on c2.id = ftopics.cat_id
where fcats.id = c2.id
order by fposts.id desc
limit 1
) as last_post_id
FROM fcats
WHERE section = "gd"
For first part of your answer you should use count distinct, and for second part a subquery:
SELECT c.id,
c.title ,
count( distinct t.cat_id) AS total_topics ,
count( distinct p.id) AS total_posts ,
(select p2.id
from ne_forum_posts p2
inner join ne_forum_topics t2 on p2.topic_id = t2.id
inner join ne_forum_sub_cats c2 on c2.id = t2.cat_id
where c2.id = c.id
order by p2.id desc
limit 1) as last_post_id
FROM ne_forum_sub_cats c LEFT OUTER JOIN
ne_forum_topics t on c.id = t.cat_id LEFT OUTER JOIN
ne_forum_posts p on p.topic_id = t.id
WHERE section = "gd"
all typos fixed and tested.