Can't get my head around GROUP_CONCAT - php

I cannot seem to get an GROUP_CONCAT query to work.
I have this tables, which I joined:
stud
id | stud_name |
-----------------
1 | Class1 |
2 | Class2 |
note
id | stud_id | mat_id | Note |
------------------------------------------------
1 | 1 | 1 | 10 |
2 | 1 | 2 | 9 |
3 | 1 | 1 | 10 |
mat
id | mat_name |
----------------
1 | Porc |
2 | Vaca |
Here is what I did to join them.
SELECT
`stud`.`id`
, `mat`.`mat_name`
, `note`.`note`
FROM
`stud`
LEFT JOIN
`note`
ON
(`stud`.`id` = `note`.`id_stud`)
LEFT JOIN
`mat`
ON
(`note`.`id_mate` = `mat`.`id`)
Here is what I want them to look.
mat.id | mat_name | Note |
-----------------------------
1 | Porc | 10,10 |
2 | Vaca | 9 |
Here is how they look.
mat.id | mat_name | Note |
-----------------------------
1 | Porc | 10 |
2 | Vaca | 9 |
1 | Port | 10 |
I tried doing this.
SELECT
`mat`.`mat_name`,
GROUP_CONCAT(`note`.`note`) AS `note`.`note`,
FROM ( "Here is what I did to join them." )attr_groups
GROUP BY `mat`.`mat_name`;
ORDER BY `mat`.`mat_name`;
Any ideas?
EDIT: If I add group by or ORDER by, no results are found. If I don't add them, the query works but its not concatenating them.

SELECT mat.id,
mat.mat_name,
GROUP_CONCAT(note.note) as note
FROM mat
LEFT JOIN note ON mat.id = note.mat_id
LEFT JOIN stud ON note.stud_id = stud.id
GROUP BY mat.id, mat.mat_name
ORDER BY mat.mat_name
SQLFiddle demo

You have error in group by.
GROUP BY `mat`.`mat_name`;
------------------------^^^---
remove ; from query
SELECT
`stud`.`id`
, `mat`.`mat_name`
, GROUP_CONCAT(`note`.`note`) AS Note
FROM
`stud`
LEFT JOIN
`note`
ON
(`stud`.`id` = `note`.`id_stud`)
LEFT JOIN
`mat`
ON
(`note`.`id_mate` = `mat`.`id`)
GROUP BY
`stud`.mat_id
ORDER BY
`mat`.`mat_name`

Try this:
SELECT
m.id,
m.mat_name,
GROUP_CONCAT(n.Note) as grades
FROM
mat m
LEFT JOIN note n
ON n.mat_id = m.id
GROUP BY
m.id

Related

How to SELECT 2 joined tables in one MySQL query?

I have 1 master_table and 2 sub_tables. I want the join the 3 columns together (but the problem is the 2 sub_tables do not have any column that share the same value) and then SELECT * based on 2 different columns from the 2 sub_tables.
I've searched and tried many ways of coding, but couldn't find a solution.
SELECT *
FROM (master INNER JOIN sub_1 ON master.id=sub_1.id WHERE sub_1.column_1 = 'Y')
AND (master INNER JOIN sub_2 ON master.id=sub_2.id WHERE sub_2.column_2 = 'Y')
ORDER BY master.id
++++++++++++++++++++++++++++++++++++++++++++++++++
* Finally, solved. See the solution at the bottom of this post. *
++++++++++++++++++++++++++++++++++++++++++++++++++
===========
Edit: explain more about my data, problem and MySQL code
I have 3 tables stored in MySQL as follow
Master_table: regist
------------------------------------------
| reg_no | firstname | lastname | submit |
------------------------------------------
| 1 | first_A | last_A | N |
| 2 | first_B | last_B | A |
| 3 | first_C | last_C | P |
| 4 | first_D | last_D | P |
| 5 | first_E | last_E | A |
| 6 | first_F | last_F | N |
| 7 | first_G | last_G | N |
| 8 | first_H | last_H | A |
------------------------------------------
Sub_1: sub_A Sub_2: sub_P
------------------------------ ------------------------------
| reg_no | A_title | reply_A | | reg_no | P_title | reply_P |
------------------------------ ------------------------------
| 2 | 222 | Y | | 3 | 333 | N |
| 5 | 555 | N | | 4 | 444 | Y |
| 8 | 888 | Y | ------------------------------
------------------------------
I want to create a query that gives result like this
----------------------------------------------------------------------------------
| reg_no | firstname | lastname | submit | A_title | reply_A | P_title | reply_P |
----------------------------------------------------------------------------------
| 2 | first_B | last_B | A | 222 | Y | | |
| 8 | first_H | last_H | A | 888 | Y | | |
| 4 | first_D | last_D | P | | | 444 | Y |
----------------------------------------------------------------------------------
or
-----------------------------------------------------------
| reg_no | firstname | lastname | submit | title | reply |
-----------------------------------------------------------
| 2 | first_B | last_B | A | 222 | Y |
| 8 | first_H | last_H | A | 888 | Y |
| 4 | first_D | last_D | P | 444 | Y |
-----------------------------------------------------------
$sql = "SELECT *
FROM (regist INNER JOIN sub_A ON regist.reg_no = sub_A.reg_no WHERE sub_A.reply_A = 'Y')
AND (regist INNER JOIN sub_P ON regist.reg_no = sub_P.reg_no WHERE sub_P.reply_P = 'Y')
ORDER BY regist.reg_no";
Expected outcome:
ECHO personal data of all registrants who got reply as 'Y'
if($row['submit']=="A") $title = $row['A_title'];
elseif($row['submit']=="P") $title = $row['P_title'];
$result = mysql_query($sql) or die(mysql_error());
while($row = mysql_fetch_array($result))
{
echo $row['reg_no']." / ".$row['firstname']." ".$row['lastname']." / ".$title."<br>";
}
Problem: my SELECT code resulted in error. The code from #GMB and #Rogue didn't error, but echo give nothing.
If it is not possible to code a query as I want, I will just modify the column names (sub_1.reply_A and sub_2.reply_P) to be the same and change the input code in other webpages. However, it would be best if there is a way because I don't know whether the 'reply' columns were used somewhere else.
========================
Solution: a little modification from #Rogue code
SELECT *
FROM master
LEFT OUTER JOIN sub_1
ON master.id=sub_1.id
LEFT OUTER JOIN sub_2
ON master.id=sub_2.id
WHERE sub_1.column_1 = 'Y'
OR sub_2.column_2 = 'Y'
ORDER BY master.id
Do you just want simple JOINs between these 3 tables ?
SELECT m.*, s1.*, s2.*
FROM master m
INNER JOIN sub_1 s1 ON m.id=s1.id AND s1.column_1 = 'Y'
INNER JOIN sub_2 s2 ON m.id=s2.id AND s2.column_2 = 'Y'
ORDER BY m.id;
If you have master records that may not exist in both sub tables, you can switch to LEFT JOIN to avoid filtering them out.
Guidelines :
typical syntax is SELECT ... FROM table1 INNER JOIN table2 ON ... INNER JOIN table3 ON...
better put all conditions related to a JOINed table in the ON clause of the join rather than in the WHERE clause
avoid SELECT * : be specific about the columns you want to select
use table aliases to make the query easier to read
You're a little off syntactically:
SELECT *
FROM master
LEFT OUTER JOIN sub_1
ON master.id=sub_1.id
LEFT OUTER JOIN sub_2
ON master.id=sub_2.id
WHERE sub_1.column_1 = 'Y'
AND sub_2.column_2 = 'Y'
ORDER BY master.id
Personally I would recommend not using SELECT * and only grabbing the data you will need. As for determining what join to use, I like to link to CodingHorror's blog post in these times.
Edit: swapped INNER to LEFT OUTER, per OP's update

mysql: combining columns from for tables

i have this 4 tables
table FRUIT, ID is Primary key
| ID | code | A | B |
--------------------------------------------
| 1 | a01 | apple1 | quava1 |
| 2 | a02 | apple2 | quava2 |
table FLOWER, ID is Primary key
| ID | code | C | D |
--------------------------------------------
| 1 | a01 | Rose1 | Plumer1 |
| 2 | a02 | Rose2 | Plumer2 |
table ANIMAL, ID is Primary key
| ID | code | E | F |
--------------------------------------------
| 1 | a01 | butterfly1 | cat1 |
| 2 | a02 | butterfly2 | cat2 |
table DAY, code is Primary key
| code | G |
-------------------------------------
| a01 | Monday |
| a02 | Tuesday|
i want to call and combined those 4 tables into like this
| ID | A | B | C | D | E | F | G |code |
--------------------------------------------------------------------------------
| 1 | apple1 | quava1 | Rose1 | Plumer1 |butterfly1 | cat1 |Monday |a01 |
| 2 | apple2 | quava2 | Rose2 | Plumer2 |butterfly2 | cat2 |Tuesday |a02 |
this is the code:
SELECT day*, fruit*, flower*, animal*
FROM day
LEFT JOIN fruit
ON day.code = fruit.code
LEFT JOIN flower
ON day.code = flower.code
LEFT JOIN flower
ON day.code = animal.code
ORDER BY day.code DESC;
it is said that column code is ambiguous. and cannot show the table.
i need help. how can i fix this?
I would do it in this manner, using Aliases and JOINs to join all the tables (including the animal table which you forgot), and of course not forgetting to include the periods . where needed:
SELECT fr.ID, fr.A, fr.B, fl.C, fl.D, a.E, a.F, d.G, fr.code
FROM `day` AS d
LEFT JOIN `fruit` AS fr ON d.code = fr.code
LEFT JOIN `flower` AS fl ON d.code = fl.code
LEFT JOIN `animal` AS a ON d.code = a.code
ORDER BY d.code ASC;
you forget to add . between column_name and * like day.*
Try This
SELECT `day`.*, fruit.*, flower.*, animal.*
FROM `day`
LEFT JOIN fruit
ON `day`.code = fruit.code
LEFT JOIN flower
ON `day`.code = flower.code
LEFT JOIN flower
ON `day`.code = animal.code
ORDER BY `day`.code DESC;
Beside the fact that you forgot a dot '.' between the table name and the *, thats becuase you are selecting id and code column from each table, you need to specify where are you selecting from when there is more then 1 options :
SELECT fruit.A,fruit.B, flower.C,flower.D, animal.E,animal.F,day.G,day.code
FROM day
LEFT JOIN fruit
ON day.code = fruit.code
LEFT JOIN flower
ON day.code = flower.code
LEFT JOIN animal
ON day.code = animal.code
ORDER BY day.code DESC;

How can I get total user in a group from database by PHP

I have 2 tables in database:
How can I get total user for each group. i.e: group 1: total are 2 users;
group2: total are 2 users;
group3: total is 1 user
You need normalization and never store comma-separated data.
Consider the following
mysql> select * from user_table ;
+---------+---------------+
| user_id | user_group_id |
+---------+---------------+
| 1 | 1,2 |
| 2 | 2 |
| 3 | 1,3 |
+---------+---------------+
3 rows in set (0.00 sec)
mysql> select * from group_table ;
+----------+------------+
| group_id | group_name |
+----------+------------+
| 1 | a |
| 2 | b |
| 3 | c |
+----------+------------+
3 rows in set (0.00 sec)
The above data is not normalized and to get the desired result out of these you need to use some in-efficient query as
select
g.group_id,
count(*) as total
from group_table g
left join user_table u on find_in_set(g.group_id,u.user_group_id) > 0
group by g.group_id ;
+----------+-------+
| group_id | total |
+----------+-------+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
+----------+-------+
Now lets do normalization and store user-group data in a different table as
mysql> select * from user_to_group ;
+---------+----------+
| user_id | group_id |
+---------+----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
| 3 | 3 |
+---------+----------+
You can easily write different queries from these tables now and here are some examples
select group_id,count(*) as tot from user_to_group group by group_id ;
+----------+-----+
| group_id | tot |
+----------+-----+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
+----------+-----+
Joining the tables would even more easy
select
g.group_id,
g.group_name,
count(*) as tot
from user_to_group ug
join group_table g on g.group_id = ug.group_id
join user_table u on u.user_id = ug.user_id
group by g.group_id
+----------+------------+-----+
| group_id | group_name | tot |
+----------+------------+-----+
| 1 | a | 2 |
| 2 | b | 2 |
| 3 | c | 1 |
+----------+------------+-----+
SELECT group_name, COUNT(*) FROM user_table u, group_table g WHERE u.user_group_id LIKE %g.group_id% GROUP BY g.group_name;
this should work and give you a list of all groups and how many users are in them.
I will recommend you to create a third table which holds the information about which users are in which groups.
CREATE TABLE users_in_groups
(
user_id INT
, group_id INT
);
Then you can join like this:
SELECT
gt.group_id
, count(ut.user_id)
FROM
user_table AS ut
, INNER JOIN users_in_groups AS uig ON uig.user_id = ut.user_id
, INNER JOIN group_table AS gt ON gt.group_id = uig.group_id
GROUP BY
gt.group_id
;
To use the table you have now will you have to do something like this (in mysql):
SELECT
gt.group_id
, count(ut.user_id)
FROM
user_table AS ut
, INNER JOIN group_table AS gt ON LOCATE(gt.group_id, ut.user_group_id) > 0
GROUP BY
gt.group_id
Remember, when using group by, always locate what makes your group unique!
This is not an answer to your specific question but rather an alternative data structure proposal that might be better.
Introduce a new table members that looks like
# members
user_id | group_id
1 | 1
1 | 2
2 | 2
3 | 1
3 | 3
Then you could SELECT group_id, count(*) FROM members GROUP BY group_id
+----------+----------+
| group_id | count(*) |
+----------+----------+
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
+----------+----------+
This structure might also make it easier for you to manage your memberships. user_id + group_id should be unique. And if supported let them be foreign keys.

Left join in codeigniter

I have 3 tables,
itemmaster
|--------|----------|
| id | name |
|--------|----------|
| 1 | Pizza |
|--------|----------|
| 2 | Burger |
|--------|----------|
| 3 | Pepsi |
---------------------
order
|--------|----------|
|orderid | date |
|--------|----------|
| 1 | 1-1-11 |
|--------|----------|
| 2 | 2-1-11 |
|--------|----------|
| 3 | 3-1-11 |
---------------------
orderdetails
|--------|-------------|---------|---------|
| id | orderid |itemid |quantity |
|--------|-------------|---------|---------|
| 1 | 1 | 1 | 10 |
|--------|-------------|---------|---------|
| 2 | 1 | 2 | 20 |
|--------|-------------|---------|---------|
| 3 | 2 | 1 | 10 |
-------------------------------------------
I want to join these 3 tables to get quantity of items of an order that placed on a particular date.
What I have tried is
$this->db->from('itemmaster');
$this->db->join('orderdetails', 'orderdetails.itemid= itemmaster.id','left');
$this->db->join('order', 'order.orderid= orderdetails.orderid');
$this->db->where('order.date',"1-1-11");
$query = $this->db->get();
I got Result as,
Pizza------ 10
Burger------10
What I want is,
Pizza-------10
Burger------20
Pepsi-------0
If changing the all the joins to left joins, you can always do it in two separate query and do a union between the two of them. One would get all the lines that actualy have a quantity and the other would get the rests and put them toguether.
It would look a bit like this. there might be some synthax error, but you'll have to rewrite it in php anyway:
p.s. to add up the quantities, you can use sum()
Select itemmaster.name, orderdetails.quantity from itemmaster
left join orderdetails on orderdetails.itemid = itemmaster.id
left join order on order.orderid = orderdetails.orderid
where order.date = '1-1-11'
group by itemmaster.name
Union
Select itemmaster.name, '0' as quantity From itemmaster
except (Select itemmaster.name, orderdetails.quantity from itemmaster
left join orderdetails on orderdetails.itemid = itemmaster.id
left join order on order.orderid = orderdetails.orderid
where order.date = '1-1-11'
group by itemmaster.name)
group by itemmaster.name
hope this helps ! good luck

PHP MYSQL query a join table for to get last row for parent table query

I have two tables : ticket & history_ticket
Table ticket :
ticket_id | ticket_desc
1 | software
2 | hardware
3 | other
Table history_ticket :
history_id | ticket_id | message | status
1 | 1 | text | process
2 | 2 | text | solve
3 | 3 | text | process
4 | 3 | text | solve
I want result like this
ticket_id | ticket_desc | status
1 | software | process
2 | hardware | solve
3 | other | solve
I've tried various joins and subselects, but no luck
Any help/directions will be much appreciated!
UPDATE : How if i change the result, like this
ticket_id | ticket_desc | last_status | count_message
1 | software | process | 1
2 | hardware | solve | 1
3 | other | solve | 2
Try this:
SELECT
t.ticket_id,
ticket_desc,
ht.status
FROM ticket AS t
INNER JOIN history_ticket AS ht ON t.ticket_id = ht.ticket_id
INNER JOIN
(
SELECT ticket_id, MAX(history_id) maxid
FROM history_ticket
GROUP BY ticket_id
) AS ht2 ON ht.history_id = ht2.maxid;
SQL Fiddle Demo
This will give you:
| TICKET_ID | TICKET_DESC | STATUS |
-------------------------------------
| 1 | software | process |
| 2 | hardware | solve |
| 3 | Problem | solve |
UPDATE 1
To get the count of messages for each ticket, you can simply include COUNT(history_id) AS sum_message in the subquery like this:
SELECT
t.ticket_id,
ticket_desc,
ht.status,
ht2.sum_message
FROM ticket AS t
INNER JOIN history_ticket ht ON t.ticket_id = ht.ticket_id
INNER JOIN
(
SELECT
ticket_id,
MAX(history_id) maxid,
COUNT(history_id) AS sum_message
FROM history_ticket
GROUP BY ticket_id
) AS ht2 ON ht.history_id = ht2.maxid;
Updated SQL Fiddle Demo
This will give you:
| TICKET_ID | TICKET_DESC | STATUS | SUM_MESSAGE |
---------------------------------------------------
| 1 | software | process | 1 |
| 2 | hardware | solve | 1 |
| 3 | Problem | solve | 2 |
Update 2
If you want to select names for the ids divisi_id, for simple values, you can use the CASE expression for this:
SELECT
t.ticket_id,
ticket_desc,
CASE
WHEN t.divisi_id = 101 THEN 'Divisi A'
WHEN t.divisi_id = 102 THEN 'Divisi B'
END AS 'Divisi',
ht.status,
ht2.sum_message
FROM ticket AS t
INNER JOIN history_ticket ht ON t.ticket_id = ht.hticket_id
INNER JOIN
(
SELECT hticket_id, MAX(history_id) maxid, COUNT(history_id) AS sum_message
FROM history_ticket
GROUP BY hticket_id
) AS ht2 ON ht.history_id = ht2.maxid;
Updated SQL Fiddle Demo
This will give you:
| TICKET_ID | TICKET_DESC | DIVISI | STATUS | SUM_MESSAGE |
--------------------------------------------------------------
| 1 | software | Divisi A | process | 1 |
| 2 | hardware | Divisi B | solve | 1 |
| 3 | Problem | Divisi A | solve | 2 |
For multiple values, you can put them in a temp table, or you can select them in a subquery and join the table to get the name like this:
SELECT
t.ticket_id,
ticket_desc,
d.Divisi,
ht.status,
ht2.sum_message
FROM ticket AS t
INNER JOIN history_ticket ht ON t.ticket_id = ht.hticket_id
INNER JOIN
(
SELECT hticket_id, MAX(history_id) maxid, COUNT(history_id) AS sum_message
FROM history_ticket
GROUP BY hticket_id
) AS ht2 ON ht.history_id = ht2.maxid
INNER JOIN
(
SELECT 101 AS divisi_id, 'Divisi A' AS Divisi
UNION ALL
SELECT 102 , 'Divisi B'
... -- here you put other values or you can join a temp table instead
) AS D ON t.divisi_id = D.divisi_id;
Updated SQL Fiddle Demo
select distinct ticket.ticket_id, ticket.ticket_desc, history_ticket.status
from ticket
join history_ticket on ticket_id
This, as far as I remember, will choose a description and status at random if you have more than one. If you want to apply a specific rule to which one to pick, give more info and we can help you on that.
Try
SELECT DISTINCT
tk.ticket_id,
tk.ticket_desc,
ht.status
FROM ticket tk JOIN history_ticket ht ON tk.ticket_id = tk.ticket_id
ORDER BY tk.ticket_id
Try,
Select distinct t.ticket_id, t.ticket_desc, h.status
from ticket t, history_ticket h
where t.ticket_id = h.ticket_id
order by t.ticket_id

Categories