Join three tables with comma separated values in mysql - mysql

I have 3 tables as below
**tbl_link**
╔═════════════╦═══════════════╦═══════════════╗
║ subject_id ║ system_csv_id ║class_csv_id ║
╠═════════════╬═══════════════╬═══════════════╣
║ 1 ║ 4,3 ║ 5,1,3,4 ║
║ 2 ║ 1,3 ║ 2,3 ║
╚═════════════╩═══════════════╩═══════════════╝
**tbl_system** **tbl_class**
╔═══════════╦════════════════════╗ ╔═══════════╦════════════════════╗
║ system_id ║ system_desc ║ ║ class_id ║ class_desc ║
╠═══════════╬════════════════════╣ ╠═══════════╬════════════════════╣
║ 1 ║ ANY ║ ║ 1 ║ GRADE 8 ║
║ 2 ║ LOCAL SYSTEM ║ ║ 2 ║ GRADE 9 ║
║ 3 ║ BRITISH SYSTEM ║ ║ 3 ║ GRADE 10 ║
║ 4 ║ AMERICAN SYSTEM ║ ║ 4 ║ GRADE 11 ║
╚═══════════╩════════════════════╝ ║ 5 ║ GRADE 12 ║
╚═══════════╩════════════════════╝
I want to query tbl_link and associate the 'system_csv_id' -> 'system_desc' and 'class_csv_id, -> 'class_desc' to reach following output.
╔═════════════╦═══════════════════╦═════════════════════════════════════════╗
║ subject_id ║ system ║ class ║
╠═════════════╬═══════════════════╬═════════════════════════════════════════╣
║ 1 ║ AMERICAN,BRITISH ║ GRADE 12, GRADE 8, GRADE 10, GRADE 11 ║
║ 2 ║ ANY, BRITISH ║ GRADE 9, GRADE 10 ║
╚═════════════╩═══════════════════╩═════════════════════════════════════════╝
I did some searches to find solution. All i got that this can be achieved using (CONCAT or GROUP_CONCAT) and ON FIND_IN_SET() but as my SQL knowledge is very limited, I am unable to achieve the required output.
Any help shall be highly appreciated.

Join the tables and use group_concat():
select l.subject_id,
group_concat(distinct s.system_desc order by find_in_set(s.system_id, l.system_csv_id)) system,
group_concat(distinct c.class_desc order by find_in_set(c.class_id, l.class_csv_id)) class
from tbl_link l
inner join tbl_system s on find_in_set(s.system_id, l.system_csv_id)
inner join tbl_class c on find_in_set(c.class_id, l.class_csv_id)
group by l.subject_id
See the demo.
Results:
| subject_id | system | class |
| ---------- | ------------------------------ | ---------------------------------- |
| 1 | AMERICAN SYSTEM,BRITISH SYSTEM | GRADE 12,GRADE 8,GRADE 10,GRADE 11 |
| 2 | ANY,BRITISH SYSTEM | GRADE 9,GRADE 10 |

Related

Efficient use of Sum in query within the same id

I am learning sql query.
Here is my table simplified
╔════╦══════════════╦══════════════╦════════╦
║ id ║ user_id ║ department_id║ salary ║
╠════╬══════════════╬══════════════╬════════╬
║ 1 ║ 1 ║ 3 ║ 100 ║
║ 2 ║ 2 ║ 3 ║ 50 ║
║ 3 ║ 1 ║ 3 ║ 30 ║
║ 4 ║ 2 ║ 3 ║ 20 ║
║ 5 ║ 2 ║ 3 ║ 20 ║
╚════╩══════════════╩══════════════╩════════╩
Here is what I want to have below
╦══════════════╦══════════════╦════════╦
║ user_id ║ department_id║ salary ║
╠══════════════╬══════════════╬════════╬
║ 1 ║ 3 ║ 130 ║
║ 2 ║ 3 ║ 90 ║
╚══════════════╩══════════════╩════════╩
I want to add user salary if they are in the same department.
I have nooo ideas how to start.
Does anyone have good feedback that I can start with?
Thank you in advance
Try this query:
Select user_id, department_id, sum(salary) from table
Group by user_id, department_id
In the select clause, you have the columns you wish to select and your group by clause contains the grouping.

how to create dynamic pivot tables in MySQL

I'm having trouble generating pivot tables in MySQL and wonder if anyone can give me any tips and some documentation so I can finally resolve this problem.
My problem is as follows:
I have 2 tables in my database. clients contains customer information such as name, ssn and some personal data. In the cli_location table are client locations: address, telephone number, mobile phone and so on.
Sometimes we need to do selects based on criteria such as location, to pick up customers in a particular city. We also make queries based on cpf to get information for a specific customer.
I often need to cross these two tables. I currently do a left join. The problem with the left join is that sometimes the client has more than one phone, which generates new lines for each one.
When researching how to improve this behavior I discovered pivot tables, however I am not able to understand them.
1st table (clients):
╔════╦══════╦═══════╦════════════╦═══════╦════════╗
║ id ║ name ║ ssn ║ dt_birth ║ store ║ value ║
╠════╬══════╬═══════╬════════════╬═══════╬════════╣
║ 1 ║ john ║ 12345 ║ 1991-11-04 ║ 318 ║ 34.33 ║
║ 2 ║ john ║ 12345 ║ 1991-11-04 ║ 318 ║ 654.44 ║
║ 3 ║ john ║ 12345 ║ 1991-11-04 ║ 212 ║ 238.00 ║
║ 4 ║ alex ║ 54321 ║ 1988-05-20 ║ 321 ║ 334.44 ║
╚════╩══════╩═══════╩════════════╩═══════╩════════╝
2st table(cli_location):
╔════╦══════╦═══════╦══════════╦════════╦═════════╦═══════╦══════════╗
║ id ║ name ║ ssn ║ address ║ city ║ state ║ zip ║ tel ║
╠════╬══════╬═══════╬══════════╬════════╬═════════╬═══════╬══════════╣
║ 1 ║ john ║ 12345 ║ street1 ║ city1 ║ state1 ║ 23443 ║ 23432122 ║
║ 2 ║ john ║ 12345 ║ street1 ║ city1 ║ state1 ║ 23443 ║ 98765434 ║
║ 3 ║ john ║ 12345 ║ street2 ║ city5 ║ state7 ║ 54323 ║ 65765567 ║
║ 4 ║ john ║ 12345 ║ street3 ║ city4 ║ state9 ║ 76543 ║ 44323455 ║
║ 5 ║ alex ║ 54321 ║ street34 ║ city30 ║ state33 ║ 43234 ║ 86643457 ║
╚════╩══════╩═══════╩══════════╩════════╩═════════╩═══════╩══════════╝
When crossing the tables using left join, with the field of reference being ssn, the result has many duplicate lines. I am not able to change the structure of the data in production.
I had thought about doing something like :
╔════╦══════╦═══════╦════════════╦═══════╦════════╦════════╦════════╦════════╦════════╦══════════╦════════╦═════════╦═══════╦══════════╦════════╦════════╦════════╦══════════╦════════╦════════╦════════╦══════════╦══════════╦══════════╦══════════╗
║ id ║ name ║ ssn ║ dt_birth ║ store ║ value ║ store1 ║ value1 ║ store2 ║ value2 ║ address ║ city ║ state ║ zip ║ address1 ║ city1 ║ state1 ║ zip1 ║ address2 ║ city2 ║ state2 ║ zip2 ║ tel ║ tel1 ║ tel2 ║ tel3 ║
╠════╬══════╬═══════╬════════════╬═══════╬════════╬════════╬════════╬════════╬════════╬══════════╬════════╬═════════╬═══════╬══════════╬════════╬════════╬════════╬══════════╬════════╬════════╬════════╬══════════╬══════════╬══════════╬══════════╣
║ 1 ║ john ║ 12345 ║ 1991-11-04 ║ 318 ║ 34.33 ║ 318 ║ 654.44 ║ 212 ║ 238.00 ║ street1 ║ city1 ║ state1 ║ 23443 ║ street2 ║ city5 ║ state7 ║ 54323 ║ street3 ║ city4 ║ state9 ║ 76543 ║ 23432122 ║ 98765434 ║ 65765567 ║ 44323455 ║
║ 4 ║ alex ║ 54321 ║ 1988-05-20 ║ 321 ║ 334.33 ║ (null) ║ (null) ║ (null) ║ (null) ║ street34 ║ city30 ║ state33 ║ 43234 ║ (null) ║ (null) ║ (null) ║ (null) ║ (null) ║ (null) ║ (null) ║ (null) ║ (null) ║ 86643457 ║ (null) ║ (null) ║
╚════╩══════╩═══════╩════════════╩═══════╩════════╩════════╩════════╩════════╩════════╩══════════╩════════╩═════════╩═══════╩══════════╩════════╩════════╩════════╩══════════╩════════╩════════╩════════╩══════════╩══════════╩══════════╩══════════╝
The idea is to generate columns dynamically as there are more lines with the same type of information, as in the above table. Even if there are 2 rows with the same store, the value of field value differs so columns must be added for each store-value pair. The same for the address and phone fields as in the table above.
If someone could point me to some documentation, show me any examples or anything that helps me learn how to do this kind of thing I would be very grateful!
Edit
Thinking about this further, I'm confident that SQL can not do exactly what you need. This answer explains why, and lists your choices:
write application code to build a query dynamically, or
run your existing left join query, and then write application code to manipulate the result into the desired format.
However, if you're comfortable combining multiple values into one column, group_concat might get you close:
select
ssn,
name,
group_concat(distinct tel separator ', ') phones,
group_concat(distinct address separator '; ') addresses
from
(select clients.ssn,
clients.name,
cli_location.tel,
concat(cli_location.address, ', ', city, ' ', zip) address
from clients
left join cli_location
on clients.ssn=cli_location.ssn) c
group by ssn
produces:
| SSN | NAME | PHONES | ADDRESSES |
|-------|------|----------------------------------------|------------------------------------------------------------------|
| 12345 | john | 23432122, 44323455, 65765567, 98765434 | street1, city1 23443; street3, city4 76543; street2, city5 54323 |
| 54321 | alex | 86643457 | street34, city30 43234 |
http://sqlfiddle.com/#!2/ca287/25
Original
If you want a list of customers in a particular city:
select clients.ssn, clients.name
from clients
inner join cli_location
using(ssn)
where city = 'city1'
group by ssn;
http://sqlfiddle.com/#!2/ca287/3
Your schema is making this more difficult than it should be though. Consider normalizing it so that client names are in only one table, and split one-to-many relationships (like client:phone) into separate tables.

Need help to retrieve data from three MySQL tables

I have three tables named "users","user_hobbies" and "hobbies". Below is the sample tables with values;Below is the users table with fields id, name and age
╔════╦══════╦═════╗
║ ID ║ NAME ║ AGE ║
╠════╬══════╬═════╣
║ 1 ║ abc ║ 23 ║
║ 2 ║ xyz ║ 24 ║
║ 3 ║ pqr ║ 21 ║
╚════╩══════╩═════╝
and below is user_hobbies table with fields id, user_id and hobby_id
╔════╦═════════╦══════════╗
║ ID ║ USER_ID ║ HOBBY_ID ║
╠════╬═════════╬══════════╣
║ 1 ║ 1 ║ 1 ║
║ 2 ║ 1 ║ 2 ║
║ 3 ║ 1 ║ 3 ║
║ 4 ║ 2 ║ 4 ║
║ 5 ║ 2 ║ 3 ║
║ 6 ║ 2 ║ 5 ║
║ 7 ║ 3 ║ 2 ║
║ 8 ║ 4 ║ 6 ║
╚════╩═════════╩══════════╝
. Below is the hobbies table with fields id and desc
╔════╦═══════════╗
║ ID ║ DESC ║
╠════╬═══════════╣
║ 1 ║ music ║
║ 2 ║ chatting ║
║ 3 ║ cricket ║
║ 4 ║ badminton ║
║ 5 ║ chess ║
║ 6 ║ cooking ║
╚════╩═══════════╝
. The actual requirement is that i need a query to retrieve name, age, hobby_id and desc (see an example below)
╔══════╦═════╦══════════╦═════════════════════════╗
║ NAME ║ AGE ║ HOBBYID ║ DESC ║
╠══════╬═════╬══════════╬═════════════════════════╣
║ abc ║ 23 ║ 1,2,3 ║ music,chatting,cricket ║
║ pqr ║ 21 ║ 2 ║ chatting ║
║ xyz ║ 24 ║ 4,3,5 ║ badminton,cricket,chess ║
╚══════╩═════╩══════════╩═════════════════════════╝
You need to join the tables first and use an aggregate function called GROUP_CONCAT().
SELECT a.Name,
a.Age,
GROUP_CONCAT(c.ID) hobbyIDs,
GROUP_CONCAT(c.desc) descList
FROM users a
INNER JOIN user_hobbies b
ON a.ID = b.user_ID
INNER JOIN hobbies c
ON b.hobby_ID = c.ID
GROUP BY a.Name, a.Age
SQLFiddle Demo
MySQL GROUP_CONCAT()
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
OUTPUT
╔══════╦═════╦══════════╦═════════════════════════╗
║ NAME ║ AGE ║ HOBBYIDS ║ DESCLIST ║
╠══════╬═════╬══════════╬═════════════════════════╣
║ abc ║ 23 ║ 1,2,3 ║ music,chatting,cricket ║
║ pqr ║ 21 ║ 2 ║ chatting ║
║ xyz ║ 24 ║ 4,3,5 ║ badminton,cricket,chess ║
╚══════╩═════╩══════════╩═════════════════════════╝

Query entire file but by separate data, grouped by each unique column element

Given a data set similar to:
╔═════════╦════════╦════════╗
║ FIELD1 ║ FIELD2 ║ FIELD3 ║
╠═════════╬════════╬════════╣
║ 11-1.01 ║ Jacob ║ 3 ║
║ 11-1.02 ║ Jacob ║ 4 ║
║ 12-2.01 ║ Jacob ║ 3 ║
║ 13-3.01 ║ Jacob ║ 4 ║
║ 13-3.02 ║ Jacob ║ 3 ║
║ 13-3.03 ║ Jacob ║ 2 ║
║ 11-1.01 ║ Chris ║ 3 ║
║ 11-1.02 ║ Chris ║ 4 ║
║ 12-2.01 ║ Chris ║ 2 ║
║ 13-3.01 ║ Chris ║ 4 ║
║ 13-3.02 ║ Chris ║ 3 ║
║ 13-3.03 ║ Chris ║ 2 ║
║ 11-1.01 ║ Mike ║ 4 ║
║ 11-1.02 ║ Mike ║ 3 ║
╚═════════╩════════╩════════╝
I need to find the average of the Field3 values for the repeating pre-decimal values of Field1 (the after decimal values are not important), for each unique Field2 element.
Field1 values are defined as type CHAR and 7 digits in length (including hyphen and decimal).
I am currently able to find the averages for one specific Field2 element by using a WHERE clause, as such:
SELECT prefix, COUNT(prefix), Field2, FORMAT(AVG(suffix),2)
FROM
(
SELECT LEFT(Field1,4) AS prefix, Field3 AS suffix, Field2
FROM mytable WHERE Field2 = 'Jacob'
)x
GROUP BY prefix;
However, it is my goal to run through the entire file and find averages for each different Field2 element, so I don't need to run the program as many times as there are unique Field2 names.
I feel like this should be a rather easy change to my current code but have no idea what to do.
There is probably a better way to structure this data in a table, although this is how I've received it and must work with it (I don't know much about it).
UPDATE 1
Desired Result
╔════════╦═════════════╦════════╦═════════╗
║ PREFIX ║ PREFIXCOUNT ║ FIELD2 ║ AVERAGE ║
╠════════╬═════════════╬════════╬═════════╣
║ 11-1 ║ 2 ║ Chris ║ 3.50 ║
║ 12-2 ║ 1 ║ Chris ║ 2.00 ║
║ 13-3 ║ 3 ║ Chris ║ 3.00 ║
║ 11-1 ║ 2 ║ Jacob ║ 3.50 ║
║ 12-2 ║ 1 ║ Jacob ║ 3.00 ║
║ 13-3 ║ 3 ║ Jacob ║ 3.00 ║
║ 11-1 ║ 2 ║ Mike ║ 3.50 ║
╚════════╩═════════════╩════════╩═════════╝
Remove the where clause on the subquery and grouped them by prefix and Field2.
SELECT prefix,
COUNT(prefix) PrefixCount,
Field2,
FORMAT(AVG(suffix),2) Average
FROM
(
SELECT LEFT(Field1,4) AS prefix,
Field3 AS suffix,
Field2
FROM TableName
) x
GROUP BY prefix, Field2
ORDER BY Field2, prefix
SQLFiddle Demo
The output,
+--------+-------------+--------+---------+
| PREFIX | PREFIXCOUNT | FIELD2 | AVERAGE |
+--------+-------------+--------+---------+
| 11-1 | 2 | Chris | 3.50 |
| 12-2 | 1 | Chris | 2.00 |
| 13-3 | 3 | Chris | 3.00 |
| 11-1 | 2 | Jacob | 3.50 |
| 12-2 | 1 | Jacob | 3.00 |
| 13-3 | 3 | Jacob | 3.00 |
| 11-1 | 2 | Mike | 3.50 |
+--------+-------------+--------+---------+

Join table to get the latest rows from unique row

I have two table
The table structure goes something like this
Table A
╔══════╦════════╗
║ P_ID ║ P_NAME ║
╠══════╬════════╣
║ 1 ║ name1 ║
║ 2 ║ name2 ║
║ 3 ║ name3 ║
║ 4 ║ name5 ║
╚══════╩════════╝
Table B
╔═════╦════════╦════════╦═══════════╦═══════╗
║ ID ║ P_ID ║ C_ID ║ C_PRICE ║ TIME ║
╠═════╬════════╬════════╬═══════════╬═══════╣
║ 1 ║ 1 ║ 3 ║ 11 ║ 11111 ║
║ 2 ║ 2 ║ 4 ║ 22 ║ 22222 ║
║ 3 ║ 3 ║ 5 ║ 33 ║ 33333 ║
║ 4 ║ 3 ║ 6 ║ 44 ║ 44444 ║
║ 5 ║ 3 ║ 6 ║ 55 ║ 55555 ║
║ 6 ║ 4 ║ 7 ║ 66 ║ 66666 ║
╚═════╩════════╩════════╩═══════════╩═══════╝
The requirement is
1. Join two table
2. Group By C_ID
3. The latest rows in Group By
I tried to modify the answer By Bill Karwin from How do I join the most recent row in one table to another table?
SELECT e.*, s1.*
FROM table_a e
INNER JOIN
table_b s1
ON (e.p_id = s1.p_id)
LEFT OUTER JOIN table_b s2
ON (e.p_id = s2.p_id AND s1.id < s2.id)
WHERE s2.p_id IS NULL;
but I could not achieve what I want. From his answer I will get
╔═════╦════════╦════════╦═══════════╦═══════╦═════════╗
║ ID ║ P_ID ║ C_ID ║ C_PRICE ║ TIME ║ P_NAME ║
╠═════╬════════╬════════╬═══════════╬═══════╬═════════╣
║ 1 ║ 1 ║ 3 ║ 11 ║ 11111 ║ name1 ║
║ 2 ║ 2 ║ 4 ║ 22 ║ 22222 ║ name2 ║
║ 5 ║ 3 ║ 6 ║ 55 ║ 55555 ║ name3 ║
║ 6 ║ 4 ║ 7 ║ 66 ║ 66666 ║ name5 ║
╚═════╩════════╩════════╩═══════════╩═══════╩═════════╝
But the output what I want is as below ( dublicate P_ID is ok but for each dublicate P_ID should not have dublicate C_ID )
╔═════╦════════╦════════╦═══════════╦═══════╦═════════╗
║ ID ║ P_ID ║ C_ID ║ C_PRICE ║ TIME ║ P_NAME ║
╠═════╬════════╬════════╬═══════════╬═══════╬═════════╣
║ 1 ║ 1 ║ 3 ║ 11 ║ 11111 ║ name1 ║
║ 2 ║ 2 ║ 4 ║ 22 ║ 22222 ║ name2 ║
║ 3 ║ 3 ║ 5 ║ 33 ║ 33333 ║ name3 ║
║ 5 ║ 3 ║ 6 ║ 55 ║ 55555 ║ name3 ║
║ 6 ║ 4 ║ 7 ║ 66 ║ 66666 ║ name5 ║
╚═════╩════════╩════════╩═══════════╩═══════╩═════════╝
SELECT e.*, s1.*
FROM table_a e INNER JOIN
(SELECT * FROM (SELECT * FROM table_b ORDER BY time DESC) temp GROUP BY c_id) s1;
The innermost ORDER BY ensures the for the same c_id the desired row always comes first, the outter GROUP BY will group on c_id, and having not specified otherwise will return the first row found for each group.
You must
Join two table
Group By C_ID
Number row for each group desc ordered by chosen field (last imply order!)
Filter by row number = 1.
This can help you for numbering rows.