Group Concat without conditionals - mysql

I'm trying to rewrite my query to use columns instead of rows. I have this query:
mysql> SELECT r.id as responseId, a.id as answerId, o.id, optionId, q.label as questionLabel, o.label as optionLabel
-> FROM answer a
-> INNER JOIN question q ON questionId = q.id
-> INNER JOIN answer_options_option ao ON a.id = ao.answerId
-> INNER JOIN `option` o ON ao.optionId = o.id
-> INNER JOIN response r ON a.responseId = r.id
-> ORDER BY r.id, a.id, o.id;
Which gives me this output:
+------------+----------+----+----------+---------------+-------------+
| responseId | answerId | id | optionId | questionLabel | optionLabel |
+------------+----------+----+----------+---------------+-------------+
| 1 | 1 | 2 | 2 | Q1 | no |
| 1 | 2 | 4 | 4 | Q2 | b |
| 2 | 3 | 1 | 1 | Q1 | yes |
| 2 | 4 | 3 | 3 | Q2 | a |
| 2 | 4 | 4 | 4 | Q2 | b |
| 2 | 4 | 5 | 5 | Q2 | c |
+------------+----------+----+----------+---------------+-------------+
But I would like to get this output:
+------------+-----------+-------+
| responseId | Q1 | Q2 |
+------------+-----------+-------+
| 1 | no | b |
| 2 | yes | a,b,c |
+------------+-----------+-------+
So I threw together this query:
mysql> SELECT r.id as responseId,
-> IF(q.label = 'Q1', GROUP_CONCAT(o.label), NULL) as Q1,
-> IF(q.label = 'Q2', GROUP_CONCAT(o.label), NULL) as Q2
-> FROM answer a
-> INNER JOIN question q ON questionId = q.id
-> INNER JOIN answer_options_option ao ON a.id = ao.answerId
-> INNER JOIN `option` o ON ao.optionId = o.id
-> INNER JOIN response r ON a.responseId = r.id
-> GROUP BY r.id;
But it gives me this output instead:
+------------+-----------+------+
| responseId | Q1 | Q2 |
+------------+-----------+------+
| 1 | no,b | NULL |
| 2 | yes,a,b,c | NULL |
+------------+-----------+------+
Is it even possible to use GROUP_CONCAT like this? Is there another way to accomplish what I'm trying to do?
Here's a Fiddle.

You need to move the GROUP_CONCAT outside the conditional so that you aggregate the options based on the question instead of just getting the entire aggregated result based on the question value:
SELECT r.id as responseId,
GROUP_CONCAT(CASE WHEN q.label = 'Q1' THEN o.label END ORDER BY o.label) as Q1,
GROUP_CONCAT(CASE WHEN q.label = 'Q2' THEN o.label END ORDER BY o.label) as Q2
FROM answer a
INNER JOIN question q ON questionId = q.id
INNER JOIN answer_options_option ao ON a.id = ao.answerId
INNER JOIN `option` o ON ao.optionId = o.id
INNER JOIN response r ON a.responseId = r.id
GROUP BY r.id;
Output:
responseId Q1 Q2
1 no b
2 yes a,b,c
Demo on dbfiddle

Related

i want to show an one particular blog have how much likes and how much comment using sql query

Blog table:
| bid | btitle |
| 29 | ...... |
| 38 | ...... |
likes table:
| lid | bid |
| 1 | 29 |
| 2 | 29 |
| 3 | 29 |
| 4 | 38 |
| 5 | 38 |
comment table
| commid | bid |
| 1 | 29 |
| 2 | 29 |
| 3 | 38 |
I had tried the following query but that will not work for me:
SELECT blog.bid,blog.btitle,COUNT(likes.lid) AS likecnt,COUNT(comment.comid) AS commentcnt FROM blog,likes,comment WHERE blog.bid=likes.bid AND blog.bid=comment.bid GROUP BY blog.bid
i want output like:
| bid | btitle | likecnt | commentcnt |
| 29 | ...... | 3 | 2 |
| 38 | ...... | 2 | 1 |
You can do left join with separate aggregation :
select b.bid, b.btitle,
coalesce(l.likecnt, 0) as likecnt,
coalesce(c.commentcnt, 0) as commentcnt
from blog b left join
(select l.bid, count(*) as likecnt
from likes l
group by l.bid
) l
on l.bid = b.bid left join
(select c.bid, count(*) as commentcnt
from comment c
group by c.bid
) c
on c.bid = l.bid;
If you want only matching bids the use INNER JOIN instead of LEFT JOIN & remove COALESCE().
Under many circumstances, correlated subqueries may be the fastest solution:
select b.bid, b.btitle,
(select count(*) from likes l where l.bid = b.bid) as num_likes,
(select count(*) from comment c where c.bid = b.bid) as num_comments
from blog b;
When is this a win performance wise. First, you want indexes on likes(bid) and comments(bid). With those indexes, it might be the fastest approach for your query.
It is particularly better if you have a where clause filtering the blogs in the outer query. It only has to do the counts for the blogs in the result set.
Use proper joins and count DISTINCT values because multiple joins increase the number of returned rows:
SELECT b.bid, b.btitle,
COUNT(DISTINCT l.lid) AS likecnt,
COUNT(DISTINCT c.comid) AS commentcnt
FROM blog b
LEFT JOIN likes l ON b.bid = l.bid
LEFT JOIN comment c ON b.bid = c.bid
GROUP BY b.bid, b.btitle
See the demo.
I use LEFT joins just in case there are no comments or likes for a post.
Results:
| bid | btitle | likecnt | commentcnt |
| --- | ------ | ------- | ---------- |
| 29 | ...... | 3 | 2 |
| 38 | ...... | 2 | 1 |

MySQL count based on condition from a different table

I have the following tables.
Table : types
--------------------
id | type
--------------------
1 | AA
--------------------
2 | BB
--------------------
3 | AA
--------------------
4 | BB
--------------------
Table : users
--------------------
id | username
--------------------
1 | abc
--------------------
2 | bcd
--------------------
3 | cde
--------------------
4 | def
--------------------
Table : methods
---------------------------------
id | user_id | details | type_id
---------------------------------
1 | 1 | detail_1 | 1
---------------------------------
2 | 1 | detail_2 | 3
---------------------------------
3 | 1 | detail_3 | 1
---------------------------------
4 | 1 | detail_4 | 3
---------------------------------
5 | 2 | detail_3 | 1
---------------------------------
6 | 2 | detail_5 | 2
---------------------------------
7 | 2 | detail_6 | 4
---------------------------------
8 | 2 | detail_2 | 3
---------------------------------
9 | 1 | detail_2 | 3
---------------------------------
10 | 1 | detail_2 | 3
---------------------------------
Desired Result :
---------------------------------------------------
UserName | No_of_AA_details | No_of_BB_details |
---------------------------------------------------
abc | 4 | 0 |
---------------------------------------------------
bcd | 2 | 2 |
---------------------------------------------------
I need to get the count of distinct details based on the type from types table.
I have tried this queries but max I am getting is all the counts and not the distinct values.
SELECT u.username,
CASE WHEN t.type = 'AA' THEN count(distinct m.details) END AS No_of_AA_details,
CASE WHEN t.type = 'BB' THEN count(distinct m.details) END AS No_of_BB_details
FROM users as u inner join methods as m on u.id = m.user_id inner join types as t on t.id = m.type_id
GROUP BY m.user_id
SELECT u.username,
SUM(t.type = 'AA') AS No_of_AA_details,
SUM(t.type = 'AA') AS No_of_BB_details
FROM users as u inner join methods as m on u.id = m.user_id inner join types as t on t.id = m.type_id
GROUP BY m.user_id
Any suggestions are welcome.
I can't test it right know but i think you had a good idea, can you try :
SELECT u.username,
m.user_id,
CASE
WHEN t.type = 'AA' THEN 1
ELSE 0
END AS No_of_AA_details,
CASE
WHEN t.type = 'BB' THEN 1
ELSE 0
END AS No_of_BB_details
FROM users as u
INNER JOIN methods as m on u.id = m.user_id
INNER JOIN types as t on t.id = m.type_id
and now you just need to do the sum :
SELECT u.username,
m.user_id,
SUM (CASE
WHEN t.type = 'AA' THEN 1
ELSE 0
END ) AS No_of_AA_details,
SUM (CASE
WHEN t.type = 'BB' THEN 1
ELSE 0
END ) AS No_of_BB_details
FROM users as u
INNER JOIN methods as m on u.id = m.user_id
INNER JOIN types as t on t.id = m.type_id
GROUP BY u.username, m.user_id

Do Multiple Left Join on condition and get non null column value

I have a table containing the last comments posted on the website, and I'd like to join a different table depending on the comment type.
Comments Table is similar to this structure:
id | type | ressource_id |
---+------+--------------+
1 | 1 | 10 |
2 | 3 | 7 |
3 | 3 | 12 |
4 | 1 | 22 |
5 | 4 | 22 |
6 | 5 | 23 |
News Table:
news_id | notes| date |
--------+------+--------------+
10 | | 2015-08-12 |
22 | | 2015-07-12 |
Tutorial Table:
tuto_id | notes| date |
--------+------+--------------+
7 | | 2015-06-15 |
12 | | 2015-05-14 |
... Similar table for type = 4, 5, 6
Now in order to get specific comments I am doing a left join on the two tables.
SELECT co.*
FROM Comments co
LEFT JOIN News n
ON co.id = n.news_id AND co.type = 1
LEFT JOIN Tutorial t
ON co.id = t.tuto_id AND co.type = 3
WHERE (co.type IN (1,3))
I am interested in getting the date from the left table. How can I include that column in output list.
Result desired: (date from joining table)
id | type | ressource_id | date |
---+------+--------------+--------------+
1 | 1 | 10 | 2015-08-12 |
2 | 3 | 7 | 2015-06-15 |
3 | 3 | 12 | 2015-05-14 |
4 | 1 | 22 | 2015-07-12 |
Thanks.
Since you will never get a date from News and Tutorial for the same comment you might go withCOALESCE`:
SELECT co.*, COALESCE(n.date,t.date)
FROM Comments co
LEFT JOIN News n
ON co.id = n.news_id AND co.type = 1
LEFT JOIN Tutorial t
ON co.id = t.tuto_id AND co.type = 3
WHERE (co.type IN (1,3))
COALESCE will return the first argument that is not null, so if there is a matching news it will return the date from News and if there is no matching news but a matching tutorial it will return the date from Tutorial.
try this:
SELECT co.*,coalesce(n.date,t.date)
FROM Comments co
LEFT JOIN News n
ON co.id = n.news_id AND co.type = 1
LEFT JOIN Tutorial t
ON co.id = t.tuto_id AND co.type = 3
WHERE (co.type IN (1,3))
and (co.id = n.news_id or co.id = t.tuto_id)
You can use a UNION operator also.
(SELECT co.*,n.date as [date]
FROM Comments co
LEFT JOIN News n
ON co.id = n.news_id AND co.type = 1)
UNION ALL
(SELECT co.*,t.date
FROM Comments co
LEFT JOIN Tutorial t
ON co.id = t.tuto_id AND co.type = 3)
ORDER BY ID

MySQL join and sum issue / combine queries

I have created a points example to demonstrate my issue.
A members can reward other members points, I am trying to build a query which will display the points a user has and points user has given.
Database Example
Members,
+----+----------+
| id | username |
+----+----------+
| 1 | user1 |
| 2 | user2 |
| 3 | user3 |
+----+----------+
Points,
+-------+--------+--------+
| IDFor | IDFrom | Pointz |
+-------+--------+--------+
| 1 | 2 | 5 |
| 1 | 2 | 5 |
| 3 | 1 | 2 |
+-------+--------+--------+
The return I am looking for is,
+-----------+--------+-------+
| username | Pointz | Given |
+-----------+--------+-------+
| user1 | 10 | 2 |
| user2 | 0 | 10 |
| user3 | 2 | 0 |
+-----------+--------+-------+
Both my queries return,
+-----------+--------+-------+
| username | Pointz | Given |
+-----------+--------+-------+
| user1 | 10 | 4 |
| user2 | 0 | 10 |
| user3 | 2 | 0 |
+-----------+--------+-------+
http://sqlfiddle.com/#!2/32ae6/6
SELECT a.`username`, sum(a.`Pointz`), sum(b.`Pointz`) FROM
(SELECT * FROM `members`
LEFT JOIN `Example`.`Points` AS p ON `members`.`id` = p.`IDFor` ) AS a
LEFT JOIN
(SELECT * FROM `members`
LEFT JOIN `Example`.`Points` AS n ON `members`.`id` = n.`IDFrom` ) AS b
ON a.id = b.id
GROUP BY a.`id`
http://sqlfiddle.com/#!2/32ae6/7
SELECT `members`.`username`,sum(p.`Pointz`),sum(n.`Pointz`)
FROM `members`
LEFT JOIN `Example`.`Points` as p ON p.`IDFor` = `members`.`id`
LEFT JOIN `Example`.`Points` as n ON n.`IDFrom` = `members`.`id`
GROUP BY `members`.`id`
Seems to be a common question that pops up but have not found a solution, all my other attempts from similar questions have not ended well, Thanks all.
This is what u want
select a.username, ifnull(Pointz, 0) Pointz, ifnull(Given, 0) Given from
(SELECT id, `username`, sum(`Pointz`) Pointz FROM `members`
LEFT JOIN `Points` ON `members`.`id` = `Points`.`IDFor` group by id) a
left join
(SELECT id, `username`, sum(`Pointz`) Given FROM `members`
LEFT JOIN `Points` ON `members`.`id` = `Points`.`IDFrom` group by id) b
on a.id = b.id
Try:
SELECT m.username,
COALESCE( pr.preceived, 0 ) as Pointz,
COALESCE( pg.pgiven, 0 ) as Given
FROM members m
LEFT JOIN ( SELECT IDFor, sum(pointz) as preceived
FROM Points
GROUP BY IDFor ) pr
ON m.id = pr.IDFor
LEFT JOIN ( SELECT IDFrom, sum(pointz) as pgiven
FROM Points
GROUP BY IDFrom ) pg
ON m.id = pg.IDFrom
http://sqlfiddle.com/#!2/32ae6/48
Try this:
SELECT m.`username`,
ifnull((SELECT sum(`Pointz`) FROM Points p
WHERE p.IDFor = m.id ), 0),
ifnull((SELECT sum(`Pointz`) FROM Points p
WHERE p.IDFrom = m.id ), 0)
FROM Members m
SQLFiddle

Mysql join on 3 tables output

I am learning joins and have the following tables.
Student
| ID | NAME |
-------------
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | D |
Pass
| ID | MARKS |
--------------
| 2 | 80 |
| 3 | 75 |
Fail
| ID | MARKS |
--------------
| 1 | 25 |
| 4 | 20 |
The output I want is this:
| NAME | MARKS |
----------------
| B | 80 |
| C | 75 |
| A | 25 |
| D | 20 |
I wrote a query like this:
select s.id,s.name,p.marks from student s
left join pass p on s.id=p.id
left join (select f.marks,f.id from fail f ) as nn on s.id=nn.id
order by marks desc;
The output I got is this:
| id | name | Marks|
--------------------
| 1 | B | 80 |
| 2 | C | 75 |
| 3 | A | Null |
| 4 | D | NUll |
Cant figure out why Null is coming. Any pointers?
You can use CASE statement for that:
SELECT Name,
CASE WHEN P.Marks IS NULL THEN f.Marks ELSE P.Marks END AS Marks
FROM Student s
LEFT JOIN Pass p ON s.ID = p.ID
LEFT JOIN Fail f ON s.ID = f.ID
ORDER BY Marks DESC;
Or you can also use IF statement:
SELECT Name,
IF(P.Marks IS NULL, F.Marks, P.Marks) AS Marks
FROM Student s
LEFT JOIN Pass p ON s.ID = p.ID
LEFT JOIN Fail f ON s.ID = f.ID
ORDER BY Marks DESC;
Output
| NAME | MARKS |
----------------
| B | 80 |
| C | 75 |
| A | 25 |
| D | 20 |
See this SQLFiddle
To learn more about JOINs see: A Visual Explanation of SQL Joins
You select only the passed marks, this is the reason of null-s appears near falied results.
If you want to select the failed marks you can use IF condition
select s.id,s.name,IF(p.marks = null, nn.marks, p.marks) as markss
from student s
left join pass p on s.id=p.id
left join fail nn on s.id=nn.id
order by markss desc;
Or you can use union of the passed and failed results.
select s.id,s.name, u.marks
from student s
left join ( (SELECT * FROM pass) UNION (SELECT * FROM fail) ) as n ON n.id = s.id
order by marks desc;
You need to understand how the different joins work to understand why you receive NULL for the marks column.
Take a look here:A Visual Explanation of SQL Joins
The relevant example for you is:
LEFT OUTER JOIN:
SELECT * FROM TableA
LEFT OUTER JOIN TableB
ON TableA.name = TableB.name
id name id name
-- ---- -- ----
1 Pirate 2 Pirate
2 Monkey null null
3 Ninja 4 Ninja
4 Spaghetti null null
The Null values you received for the marks column are rows that have no match in the left joined tables.
(the left part of the Venn diagram) the values that does have a value are the cross section between the tow groups of the Venn Diagram.
specifics for your example:
select s.id,s.name,p.marks
from student s
left join pass p on s.id=p.id
left join (select f.marks,f.id from fail f ) as nn on s.id=nn.id
order by marks desc;
The output i got is this:
id | name | Marks
-------------------
1 | B | 80
2 | C | 75
3 | A | Null
4 | D | NUll
This will return all student rows when.
students that have a passing gtade will display the grade and thous who don't will display null.
Try the below Query, use COALESCE
select s.id,s.name,COALESCE(p.marks , nn.marks) as marks
from student s
left join pass p on s.id=p.id
left join fail nn on s.id=nn.id
order by marks desc;
SQL Fiddle