Join multiple SQL tables [duplicate] - mysql

This question already has answers here:
MySQL pivot row into dynamic number of columns
(1 answer)
SQL joining multiple tables
(4 answers)
Closed 6 years ago.
Not working
SELECT a.name, atn.name
FROM t1 a
JOIN t2 ap ON a.id = ap.area_id
JOIN t3 atn ON atn.id = ap.parent_id
I have a table t1 with area names and their type (like pin, ward and simple area name) and table t2 with their mapping and table t3 with type name and their id's.
I want a result with three columns (area name, pin, ward) i.e the result should be which area comes under pin and ward.
t1:
--------------------------
| id | area name | type |
---------------------------
| 1 | a | 5 |
| 2 | b | 8 |
| 3 | x | 7 |
| 4 | z | 8 |
| 5 | pq | 8 |
---------------------------
t2:
------------------------------
| id | area_id | parent_id |
------------------------------
| 1 | 2 | 1 |
| 2 | 2 | 3 |
| 3 | 4 | 1 |
| 4 | 5 | 3 |
-----------------------------
t3:
------------------
| id | name |
------------------
| 5 | pin |
| 7 | ward |
| 8 | area |
------------------
Result:
--------------------------
| area | pin | ward |
--------------------------
| b | a | x |
| z | a | |
| pq | | x |
--------------------------
Anybody knows how to get this, please help me. I don't know how to get that value. I tried but couldn't find anything.

Just a guess. Pivoting parent name by parent type
SELECT a.name,
max(case when atn.name = 'pin' then p.name end) as pin
max(case when atn.name = 'ward' then p.name end) as ward
FROM t2 ap
JOIN t1 a ON a.id = ap.area_id
JOIN t1 p ON p.id = ap.parent_id
JOIN t3 atn ON atn.id = p.type
GROUP BY a.name

Related

Selecting COUNT and MAX columns with 2 tables and a bridge table

so what I am trying to do is having 3 tables (pictures, collections, and bridge) with the following columns:
Collections Table:
| id | name |
------------------
| 1 | coll1 |
| 2 | coll2 |
------------------
Pictures Table: (timestamps are unix timestamps)
| id | name | timestamp |
-------------------------
| 5 | Pic5 | 1 |
| 6 | Pic6 | 19 |
| 7 | Pic7 | 3 |
| 8 | Pic8 | 892 |
| 9 | Pic9 | 4 |
-------------------------
Bridge Table:
| id | collection | picture |
-----------------------------
| 1 | 1 | 5 |
| 2 | 1 | 6 |
| 3 | 1 | 7 |
| 4 | 1 | 8 |
| 5 | 2 | 5 |
| 6 | 2 | 9 |
| 7 | 2 | 7 |
-----------------------------
And the result should look like this:
| collection_name | picture_count | newest_picture |
----------------------------------------------------
| coll1 | 4 | 8 |
| coll2 | 3 | 9 |
----------------------------------------------------
newest_picture should always be the picture with the heighest timestamp in that collection and I also want to sort the result by it. picture_count is obviously the count of picture in that collection.
Can this be done in a single statement with table joins and if yes:
how can I do this the best way?
A simple method uses correlated subqueries:
select c.*,
(select count(*)
from bridge b
where b.collection = c.id
) as pic_count,
(select p.id
from bridge b join
pictures p
on b.picture = b.id
where b.collection = c.id
order by p.timestamp desc
limit 1
) as most_recent_picture
from collections c;
A more common approach would use window functions:
select c.id, c.name, count(bp.collection), bp.most_recent_picture
from collections c left join
(select b.*,
first_value(p.id) over (partition by b.collection order by p.timestamp desc) as most_recent_picture
from bridge b join
pictures p
on b.picture = p.id
) bp
on bp.collection = c.id
group by c.id, c.name, bp.most_recent_picture;

Join records with lowest value

I have two tables, users and survey. I want query the table user and to join the table survey in a way that only the survey record with the lowest value is returned for each record in user table.
I want to avoid subqueries and temporary tables.
table users:
--------------
| uid | name |
--------------
| 1 | mike |
| 2 | john |
| 3 | bill |
--------------
table survey:
----------------------
| id | uid | value |
----------------------
| 1 | 3 | 9 |
| 2 | 3 | 5 |
| 3 | 1 | 3 |
| 4 | 1 | 7 |
| 5 | 1 | 2 |
| 6 | 2 | 4 |
| 7 | 2 | 9 |
| 8 | 1 | 0 |
| 9 | 2 | 5 |
---------------------
expected output:
---------------------
| id | name | value |
---------------------
| 8 | mike | 0 |
| 2 | bill | 5 |
| 6 | john | 4 |
---------------------
What kind of JOIn should I do, or how should I write the query?
The following query gets all rows with minimum value (doesn't exist another survey with value under the selected value)
Try this:
SELECT u.*, s.value
FROM survey s
JOIN users u
ON s.uid = u.uid
WHERE NOT EXISTS(
SELECT 'maximum'
FROM survey s2
WHERE s2.uid = s.uid
AND s2.value < s.value)
You could use something like this:
select s.id, u.name, y.min_value
from
(
select uid, min(value) as min_value
from survey
group by uid
) y
join survey s
on s.value = y.min_value
and s.uid = y.uid
join user u
on u.uid = y.uid
I think this will help you
SELECT * FROM SURVEY S
INNER JOIN USERS U
ON S.UID=U.UID
QUALIFY ROW_NUMBER() OVER (PARTITION BY S.UID ORDER BY S.VALUE1 ASC )=1;

MySQL Data Summary/Counts Table

I'm trying to put together a summary table that has counts of types of mail sent by group.
Hopefully the below is enough to explain what I mean.
Table 1 (senders)
| id | name | group_id |
+----+------+----------+
| 1 | mike | 1 |
| 2 | john | 1 |
| 3 | lucy | 2 |
| 4 | lobo | 3 |
Table 2 (mail)
| id | type | sender_id |
+----+----------+-----------+
| 1 | letter | 1 |
| 2 | postcard | 2 |
| 3 | postcard | 1 |
| 4 | letter | 2 |
| 5 | postcard | 2 |
| 6 | postcard | 4 |
Table 3 (groups)
| id | name | active |
+----+-------+--------+
| 1 | alpha | 1 |
| 2 | black | 1 |
| 3 | cero | 0 |
Ideal result
| group | letter | postcard | parcel |
+-------+--------+----------+--------+
| alpha | 2 | 3 | 0 |
| black | 0 | 0 | 0 |
So I need to get counts per mail type for active groups.
I've been working through examples (only learning MySQL) but when I think of this situation I'm just totally blank.
Have looked at the answers to Joining three tables to get summary data in MySQL but I don't quite understand how to translate the answers to my problem.
Any help is appreciated.
SELECT t.name,
MAX(CASE t.TYPE WHEN 'letter' THEN #CS:=#CS+1 ELSE 0 END ) letter,
MAX(CASE t.TYPE WHEN 'postcard' THEN #CS1:=#CS1+1 ELSE 0 END ) postcard ,
MAX(CASE t.TYPE WHEN 'parcel ' THEN #CS2:=#CS2+1 ELSE 0 END ) parcel
FROM
(SELECT
groups. name,
mail.type
FROM
groups
LEFT JOIN senders ON groups.id = senders.id
LEFT JOIN mail ON senders.id = mail.sender_id ) AS t
,(SELECT #CS:=0) CS ,(SELECT #CS1:=0) CS1 ,(SELECT #CS2:=0) CS2
You put this query
Select count(*) from senders s inner join mail m on s.id = s.sender_id inner join
groups g on s.groups_id = g.id group by m.type

Getting specific values from many-to-many relationships

My database looks like this, I have client accounts which are assigned to specific profiles, and I have profiles which are assigned to specific categories, like in this schema:
| categories | | profiles | | categories_map |
--------------- ------------- ----------------------------
| ID | name | | ID | name | | ID | profile_id | cat_id |
--------------- ------------- ----------------------------
| 1 | cat1 | | 1 | p1 | | 1 | 1 | 1 |
| 2 | cat2 | | 2 | p2 | | 2 | 2 | 1 |
| 3 | cat3 | | 3 | p3 | | 3 | 3 | 1 |
| 4 | p4 | | 4 | 1 | 2 |
| 5 | 3 | 2 |
| 6 | 4 | 3 |
| profiles_map |
-----------------------------
| ID | profile_id | acc_id |
-----------------------------
| 1 | 1 | 1 |
| 2 | 3 | 1 |
| 3 | 4 | 1 |
I need to get categories assigned to accounts - which means when I want to get categories for acc_id = 1, I should get categories with ID 2 and 3 ( category with ID 2 doesn't fit because it contains profile with ID 2 which isn't assigned to this account). I tried this query but it doesn't work
select cats.id from profiles_map map
right join categories_map catm on catm.profile_id = map.profile_id
right join categories cats on cats.id = catm.cat_id
where catm.profile_id in (select profile_id from profiles_map where acc_id = 1)
and map.acc_id = 1 group by cats.id;
Could anybody help me with this question?
Can you try this one?
SELECT DISTINCT C.ID
FROM profiles_map PM
INNER JOIN categories_map CM ON CM.profile_id = PM.profile_id
INNER JOIN categories C ON C.ID = CM.cat_id
WHERE PM.acc_id= 1
If you want to get only category id, Please try following query:
SELECT DISTINCT cm.cat_id FROM categories_map cm
WHERE cm.profile_id in
(SELECT profile_id FROM profiles_map WHERE acc_id = 1)
Or if want to get category name and id then , use following query:
SELECT cat.id,cat.name FROM categories cm
WHERE cat.id in (SELECT DISTINCT cm.cat_id FROM categories_map cm
WHERE cm.profile_id in
(SELECT pm.profile_id FROM profiles_map pm WHERE pm.acc_id = 1))

MySQL Union on Dissimilar Fields without Dummy Columns

So lets say I have 2 or more tables consisting of dissimilar columns in which a shared key (id) is not necessarily present :
Alpha:
+----+-------+-------+-------+
| id | paula | randy | simon |
+----+-------+-------+-------+
| 1 | 8 | 7 | 2 |
| 2 | 9 | 6 | 2 |
| 3 | 10 | 5 | 2 |
+----+-------+-------+-------+
Beta:
+----+---------+-----+------------+------+
| id | is_nice | sex | dob | gift |
+----+---------+-----+------------+------+
| 2 | 1 | F | 1990-05-25 | iPod |
| 3 | 0 | M | 1990-05-25 | coal |
+----+---------+-----+------------+------+
Gamma:
+----+---------+--------+
| id | is_tall | is_fat |
+----+---------+--------+
| 1 | 1 | 1 |
| 99 | 0 | 1 |
+----+---------+--------+
The desired effect is to mash the tables together on id inserting NULLs where data is not available:
+----+-------+-------+-------+---------+-----+------------+------+---------+--------+
| id | paula | randy | simon | is_nice | sex | dob | gift | is_tall | is_fat |
+----+-------+-------+-------+---------+-----+------------+------+---------+--------+
| 1 | 8 | 7 | 2 | | | | | 1 | 1 |
| 2 | 9 | 6 | 2 | 1 | F | 1990-05-25 | iPod | | |
| 3 | 10 | 5 | 2 | 0 | M | 1990-05-25 | coal | 1 | 1 |
| 99 | | | | | | | | 0 | 0 |
+----+-------+-------+-------+---------+-----+------------+------+---------+--------+
I can use NULL 'dummy' columns and UNION (MySql SELECT union for different columns?) but that seems like a royal pain if the number of tables is great. I'd like to think there is a JOIN method I can use to accomplish this, but I need some help to figure this out.
This works:
SELECT `id`, `paula`, `randy`, ..., NULL AS `is_nice`, ... FROM `Alpha`
UNION SELECT `id`, NULL AS `paula`, ..., FROM `Beta`
UNION SELECT `id`, NULL AS `paula`, ..., `is_fat` FROM `Gamma` ;
but it sure feels like the wrong way to do it. How can I get the same results without having to edit lines and lines of SQL inserting NULL AS whatever all over the place whenever I want to tack on additional tables?
Thanks in advance!
SELECT
allid.id
, a.paula, a.randy a.simon
, b. ...
, c. ...
FROM
( SELECT id
FROM Alpha
UNION
SELECT id
FROM Beta
UNION
SELECT id
FROM Gamma
) AS allid
LEFT JOIN
Alpha AS a
ON a.id = allid.id
LEFT JOIN
Beta AS b
ON b.id = allid.id
LEFT JOIN
Gamma AS g
ON g.id = allid.id
If the tables share no other column except the id, you could use the simple to write (but easier to break):
SELECT
*
FROM
( SELECT id
FROM Alpha
UNION
SELECT id
FROM Beta
UNION
SELECT id
FROM Gamma
) AS allid
NATURAL LEFT JOIN
Alpha
NATURAL LEFT JOIN
Beta
NATURAL LEFT JOIN
Gamma
You want to use LEFT JOINs.
http://dev.mysql.com/doc/refman/5.0/en/join.html
In your example:
SELECT id_t.id, a.paula, a.randy, a.simon, b.is_nice, b.sex, b.dob, b.gift, g.is_tall, g.is_fat
FROM (SELECT DISTINCT id FROM alpha,beta,gamma) as id_t
LEFT JOIN alpha a ON a.id = id_t.id
LEFT JOIN beta b on b.id = id_t.id
LEFT JOIN gamma g on g.id = id_t.id