Retrieving values from a table using JOIN [mySQL] [duplicate] - mysql

Say I have two tables in a MySQL Database.
Table 1:
ID Name
1 Jim
2 Bob
Table 2:
ID Place Race_Number
1 2nd 1
1 3rd 2
1 4th 3
2 1st 1
2 2nd 2
2 2nd 3
When selecting rows from the database, is there any way to join rows from the second table as columns to the first table? Currently I am using SELECT * FROM Table1 NATURAL JOIN Table2.
This outputs:
ID Name Place Race_Number
1 Jim 2nd 1
1 Jim 3rd 2
1 Jim 4th 3
2 Bob 1st 1
2 Bob 2nd 2
2 Bob 2nd 3
Currently I am sorting through this in my PHP script to sort it into an array. This is a pain, as I have to look at the IDs and see if they're the same and then sort accordingly. I feel like there is a way to do this right in MySQL, without having to sort it into an array in the PHP. There can be an unlimited number of entries in the second table for each ID.
The desired result right from the MySQL query is:
ID Name Race1 Race2 Race3
1 Jim 2nd 3rd 4th
2 Bob 1st 2nd 2nd
I can't make columns for Race1, Race2 etc in the table themselves because there can be an unlimited number of races for each ID.
Thanks for any help!

An INNER JOIN will suffice your needs. MySQL has no PIVOT function by you can still simulate it using CASE and MAX() function.
SELECT a.ID, a.NAME,
MAX(CASE WHEN b.Race_Number = 1 THEN b.Place ELSE NULL END) Race1,
MAX(CASE WHEN b.Race_Number = 2 THEN b.Place ELSE NULL END) Race2,
MAX(CASE WHEN b.Race_Number = 3 THEN b.Place ELSE NULL END) Race3
FROM Table1 a
INNER JOIN Table2 b
ON a.ID = b.ID
GROUP BY a.ID, a.Name
But if you have unknown number of RACE, then a DYNAMIC SQL is much more preferred.
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT('MAX(CASE WHEN b.Race_Number = ', Race_Number,
' THEN b.Place END) AS ', CONCAT('`Race', Race_Number, '`'))
) INTO #sql
FROM Table2;
SET #sql = CONCAT('SELECT s.Student_name, ', #sql, '
FROM Table1 a
LEFT JOIN Table2 b
ON ON a.ID = b.ID
GROUP BY a.ID, a.Name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Related

SQL/Presto: how to choose rows if the values match with another table's

I have 2 tables:
table 1:
task cnt
1 4
2 5
3 6
table 2:
task cnt2
1 7
2 5
3 6
4 3
I want to add a column for table 2 such that if cnt in table1 for a task is the same as cnt2 for a task in table2. If there is no match, mark it as 'no match'
desired result:
task cnt2 if_matched
1 7 'no match'
2 5 'yes'
3 6 'yes'
4 3 'no match'
I started from a query like the one below to pick tasks that have matched values
select task from table1 where table1.cnt = table2.cnt2
but I got an error for the where part.
Use left join and case expression to calculate matched:
select t2.task, t2.cnt,
case when t1.task is null then 'no match' else 'yes' end as matched
from table2 t2
left join table1 t1 on t1.task=t2.task and t1.cnt = t2.cnt2
I would recommend exists. Both Presto and MySQL support boolean expressions, so you can use:
select t2.*,
(exists (select 1 from table1 t1 where t1.task = t2.task and t1.cnt = t2.cnt)
) as t1_matches
from table2 t2;
You can use a case expression to convert this to a string, but I prefer a boolean flag.
Note: A left join can multiply the number of rows if there are multiple matches in table1. That is the reason why I recommend exists.

query getting record with special flag from two queries

I have two tables as following .I need a result shown as below.
table1
id name
-----------------
1 john
2 raju
3 gopi
4 sarath
table2
userid status
------------------
1 E
3 E
I need a query to select record from table like following
id name flag
---------------------
1 john In
2 raju Out
3 gopi In
4 sarath Out
If user having status 'E' will show in the result set as 'In' and others as 'Out'
A simple join and a case
SELECT table1.*,
CASE WHEN table2.status='E' THEN 'In' ELSE 'Out' END
FROM table1 LEFT JOIN table2 on table1.id = table2.userid;
i think below SQL will useful to you.
select table1.id, table1.name, CASE WHEN table2.status='E' THEN 'In' ELSE 'Out' END
from table1 JOIN table2 on table1.id = table2.userid;

mysql filtering same name with multiple records(different states)

I have a table where I can store the same name with different states.
|ID|name|state
1 A 1
2 A 2
3 B 3
4 C 1
There 3 states 1,2,3.
How can I find those records which has no state 3, maximum 2?
In this example A and C has no state 3 so they would be the result of the query:
SELECT * FROM `records` WHERE (`state_id`!=3)
It only returns rows without state 3 but it can be that value.
http://www.sqlfiddle.com/#!9/35dbe/2
SELECT r.*
FROM `records` r
LEFT JOIN `records` r3
ON r.name = r3.name
AND r3.state=3
WHERE r3.state IS NULL
How about a DISTINCT and NOT IN?
Something like
SELECT DISTINCT Name
FROM Table1 t
WHERE Name NOT IN (
SELECT Name
FROM Table1 s
WHERE s.State = 3)
SQL Fiddle DEMO
You can do this with conditional aggregation:
select name from t
group by name
having sum(case when state = 3 then 1 else 0 end) = 0
Fiddle http://sqlfiddle.com/#!9/a8bb5/2

MySQL - Help me construct this query

I have a relational DB that I can't think of how to form this query.
Here's the info
Table1
id name
1 Mike
Table2
id table_1_id value setting
1 1 something setting1
2 1 something2 setting2
2 1 something3 setting3
Currently, this is my sql query
SELECT * FROM Table1
JOIN Table2 on Table2.table_1_id = Table1.id
What this outputs is something like this
id name table_1_id value setting
1 Mike 1 something1 setting1
1 Mike 1 something2 setting2
1 Mike 1 something3 setting3
Is it possible to construct this in such a way to return these results so I can export it to a CSV file?
id name table_1_id something1 something2 something3
1 Mike 1 setting1 setting2 setting3
SELECT
Table1.*,
something1Table.setting AS something1,
something2Table.setting AS something2,
something3Table.setting AS something3
FROM Table1
JOIN Table2 AS something1Table ON something1Table.table_1_id = Table1.id AND something1Table.value = 'something'
JOIN Table2 AS something2Table ON something2Table.table_1_id = Table1.id AND something2Table.value = 'something2'
JOIN Table2 AS something3Table ON something3Table.table_1_id = Table1.id AND something3Table.value = 'something3'
You need a conditional aggregation:
select table1.id, table1.name,
max(case when value = 'something1' then setting end) as setting1,
max(case when value = 'something2' then setting end) as setting2,
max(case when value = 'something3' then setting end) as setting3
from table1 join
table2
on table1.id = table2.id
group by table1.id, table1.name
This type of data transformation is known an a pivot but MySQL does not have a pivot function. So you will want to replicate it using an aggregate function with a CASE expression.
If you know the the number of values ahead of time, then you can hard-code your query similar to this:
select t1.id,
t1.name,
max(case when t2.value = 'something' then t2.setting end) as setting1,
max(case when t2.value = 'something2' then t2.setting end) as setting2,
max(case when t2.value = 'something3' then t2.setting end) as setting3
from table1 t1
left join table2 t2
on t1.id = t2.table_1_id
group by t1.id, t1.name;
See SQL Fiddle with Demo
But if you have an unknown number of values that you want to transform into columns, then you can use a prepared statement to generate dynamic sql.
The query would be similar to this:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when t2.value = ''',
value,
''' then t2.setting end) AS `',
value, '`'
)
) INTO #sql
FROM table2;
SET #sql = CONCAT('SELECT t1.id,
t1.name, ', #sql, '
FROM table1 t1
left join table2 t2
on t1.id = t2.table_1_id
group by t1.id, t1.name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo
The result of both versions is:
| ID | NAME | SOMETHING | SOMETHING2 | SOMETHING3 |
---------------------------------------------------
| 1 | Mike | setting1 | setting2 | setting3 |
GROUP_CONCAT may be of use. It doesn't give you exactly what you want, because it would put the concatenated values into a single field. But depending on what you're actually trying to accomplish, perhaps you can work around that. The advantage of the GROUP_CONCAT is that it can handle any number of table2 rows per table1 row, whereas the conditional aggregation above hardwires having three entries (which may well be what you want).
SELECT table1.*,
GROUP_CONCAT(value) AS value_group,
GROUP_CONCAT(setting) AS setting_group
FROM table1
INNER JOIN table2
ON table2.table_1_id = table1.id
returns
id,person,value_group,setting_group
1,Mike,"something1,something2,something3","setting1,setting2,setting3"

mysql group by with multi rows from one table [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
mysql converting multiple rows into columns in a single row
i have a mysql table like this:
id | p | c | v
1 p1 10 1
2 p1 20 2
3 p1 30 3
4 p2 40 1
5 p2 50 2
6 p2 60 3
now i need to run a sql and get result like this:
p | as_c1 | as_c2 | as_c3
p1 10 20 30
p2 40 50 60
i used this query but it's not enough:
select
p,
c as as_c1,
c as as_c2,
c as as_c3
from test_tbl group by p, c
i searched every where, is this possible? i just need some guide.
This is basically a PIVOT that you are trying to perform. Unfortunately, MySQL does not have a PIVOT function. There are two ways to do this static or dynamic. If you know the values that you want to transform into columns, then you can use a static version but if the values are unknown then you can use a prepared statement to generate this dynamically:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(case when v = ''',
v,
''' then c end) AS as_c',
v
)
) INTO #sql
FROM table1;
SET #sql = CONCAT('SELECT p, ', #sql, '
FROM table1
GROUP BY p');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo
The static version would be similar to this:
select p,
sum(case when v=1 then c end) as_c1,
sum(case when v=2 then c end) as_c2,
sum(case when v=3 then c end) as_c3
from table1
group by p
See SQL Fiddle with Demo
SELECT p,
( CASE WHEN v = 1 THEN c ELSE NULL END ) AS as_c1,
( CASE WHEN v = 2 THEN c ELSE NULL END ) AS as_c2,
( CASE WHEN v = 3 THEN c ELSE NULL END ) AS as_c3
FROM `test_tbl`
GROUP BY p;
I think that ought to do it.