How to select and join into multiple tables with row names - mysql

Billing table
| mainid | subID | subid_name | subid_value |
|----------|---------|-------------|--------------|
| 100 | 3478 | name | Ali Baba |
| 100 | 2373 | school_type | ghetto |
| 100 | 2989 | school_loc | 41000 |
| 100 | 9824 | fee_sem | 40 |
| 100 | 283 | desc | thieves |
| 100 | 32383 | CGPA_grade | 2.9 |
Hi all,
I'm trying to work from this table (let's call it the billing table) and inner/left join into other tables based on the subid_values obtained from multiple where conditions.
For example using the subid_name = school_loc returns subid_value of 41000, which from here i can get more information about the school location if i join it into the location_info table.
However the problem comes when I also need to return values like fee_sem and CGPA_grade and school_type from the original billing table as part of the query result as I have already used the "where subid_name = school_loc".
I'd like to also join into other tables based on different subid_values that are based on different subid_name(s) like school_type, fee_sem, CGPA_grade
I have tried and cried trying to self-join into itself based on the main ID, I also tried to do the nested select without much success. I was told that this is how the data will be and there will be no change of the developer correcting the table structure which should have been transposed and columnised in the first place. Usually MS SQL and better structured DB pose no issue, however this was done on a MySQL DB which I'm not that good at using MySQL, but with the row data which should have been in columns, I need to ask for help.
select * from billing
where main_id = 100
and subid_name = **'school_loc'**
so this will return one line only with subid_value = 41000
I would inner join this subID value with location_info table and select more columns (location_name, location_state) from location_info table by using the value of billing.subid_value = 41000
select billing.mainID
,billing.subID
,billing.subid_value as School_Postcode
,location_info.location_name
,location.info,location_state
from dbo.billing
inner join dbo.location_info on billing.subid_value = location_info.ID
where subid_name is billing.school_loc
OK first inner join is done (based on where billing.subid_name = school_loc).
I'm stuck here where I need to combine the below with different where of subid_name and expecting different subid_value and taking the resultant subid_value into an inner join
select billing.mainID
,billing.subID
,billing.subid_value as Fee_Class
,fee_ranking.FeeAffordable
,fee_ranking.FeeSubsidy
inner join dbo.fee_ranking on billing.subid_value = fee_ranking.class
where billing.main_id = 100
and billing.subid_name = **'fee_sem'**
so this will return one line only (the one with fee_sem = 40)
(combining with another inner join into fee_ranking_table)
I would also like to combine more than one where of subid_name and expecting different subid_value and taking the resultant subid_value into an inner join
select billing.mainID
,billing.subID
,billing.subid_value as CGPA_Score
,CGPA_ranking.IsSmart
,CGPA_ranking.IsHardToEnter
inner join dbo.CGPA_ranking on billing.subid_value = CGPA_ranking.score
where billing.main_id = 100
and billing.subid_name = **'CGPA_grade'**
so this will return one line only (the one with CGPA_grade = 2.9)
I'm trying to achieve the output of
| mainid(from billing) | school_postcode | location_name (from location_info) | location_state (from location_info) | Fee_Class | FeeAffordable (from fee_ranking) | CGPA_Score | IsSmart (from CGPA_ranking) |
-----------------------|-----------------|------------------------------------|-------------------------------------|-----------|----------------------------------|------------|-----------------------------|
|100 |41000 |Boston |MA |40 |False |2.9 |False |

It's a bit tedious but you just have to have as many joins (or possibly left joins) as there are subid_names (or possibly subids) in your data assigning an alias to each so that you can add data from other tables.
with cte as
(select mainid from t where subid_name = 'school_loc')
select cte.mainid,t1.subid_value,t2.subid_value,t3.subid_value,
t4.subid_value,t5.subid_value,t6.subid_value
from cte
join t t1 on t1.mainid = cte.mainid and t1.subid_name = 'name'
join t t2 on t2.mainid = cte.mainid and t2.subid_name = 'school_type'
join t t3 on t3.mainid = cte.mainid and t3.subid_name = 'school_loc'
join t t4 on t4.mainid = cte.mainid and t4.subid_name = 'fee_sem'
join t t5 on t5.mainid = cte.mainid and t5.subid_name = 'desc'
join t t6 on t6.mainid = cte.mainid and t6.subid_name = 'cgpa_grade';
+--------+-------------+-------------+-------------+-------------+-------------+-------------+
| mainid | subid_value | subid_value | subid_value | subid_value | subid_value | subid_value |
+--------+-------------+-------------+-------------+-------------+-------------+-------------+
| 100 | Ali Baba | ghetto | 41000 | 40 | thieves | 2.9 |
+--------+-------------+-------------+-------------+-------------+-------------+-------------+
1 row in set (0.001 sec)

Related

How to match more than one rows using INNER JOIN with MySQL?

What is the right way to select films which labels are 'Action' AND 'Drama' using INNER JOIN ?
I've tried this query, the result must be 'Taken, The Godfather' but, no result returned.
SELECT
f.film_guid,
f.film_name
FROM
films as f
INNER JOIN
film_labels as l ON l.film_guid = f.film_guid
WHERE
l.label = 'Action' AND l.label = 'Drama'
Table: films
+------------+----------------+
| film_guid | film_name |
+------------+----------------+
| filmguid_1 | Taken |
| filmguid_2 | Matrix |
| filmguid_3 | The Godfather |
+------------+----------------+
Table: film_labels
+------------+----------------+
| film_guid | label |
+------------+----------------+
| filmguid_1 | Action |
| filmguid_1 | Drama |
| filmguid_1 | Family |
| filmguid_2 | Action |
| filmguid_3 | Action |
| filmguid_3 | Drama |
+------------+----------------+
You are looking for a rows in film_labels that contains both Action and Drama, which cannot happen. You need to search across labels that correspond to the given film, which suggest aggregation:
SELECT f.film_guid, f.film_name
FROM films as f
INNER JOIN film_labels as l ON l.film_guid = f.film_guid
WHERE l.label IN ('Action', 'Drama') -- either one, or the other
GROUP BY f.film_guid, f.film_name
HAVING COUNT(*) = 2 -- both match
Note that you could also use exists with correlated subquery. It is a bit longer to type but could be more efficient (with the right indexes in place), since it avoids the need for aggregation:
SELECT f.*
FROM films as f
WHERE
EXISTS (SELECT 1 FROM film_labels l WHERE l.film_guid = f.film_guid AND l.label = 'Action')
AND EXISTS (SELECT 1 FROM film_labels l WHERE l.film_guid = f.film_guid AND l.label = 'Drama')
For performance with the second query, you want an index on film_labels(film_guid , label).

MySQL - Working Back Through Multiple Tables by FK

I have a database which isn't of my own creation. I need to extract some specific data from it but I'm struggling to get my head around how to get the data back without doing multiple queries and looping over the result set in my code. I've looked around at other questions but haven't been able to get very far.
My data structure is (very condensed with non-relevant rows and columns omitted):
MyDb.Source
+--------+-------------+--------------------------------------+
| ID | SOURCE_TYPE | URL |
+--------+-------------+--------------------------------------+
| 10 | 3 | https://en.wikipedia.org |
+--------+-------------+--------------------------------------+
MyDb.Resource
+--------+------------+--------------------------------------+
| ID | SOURCE_FK | IDENTIFIER |
+--------+------------+--------------------------------------+
| 1 | 10 | All_Saints_Church,_Marple |
+--------+------------+--------------------------------------+
MyDb.Item_Base
+--------+-------------+--------------------------------------+
| ID | RESOURCE_FK | ITEM_TITLE |
+--------+-------------+--------------------------------------+
| 55 | 1 | All Saints Church, Marple |
+--------+-------------+--------------------------------------+
MyDb.Item
+--------+-------------+--------------------------------------+
| ID | BASE_FK | ITEM_DESCRIPTION |
+--------+-------------+--------------------------------------+
| 120 | 55 | Foo bar |
+--------+-------------+--------------------------------------+
Source - Resource is 1 to many.
Resource - Base is 1 to 1.
Item_Base - Item is 1 to 1.
What am I trying to do?
I want as few queries as possible to work back from MyDb.Source to find all items related to it. The only information I have in my hand is the ID for the source, which is 10. I want to end up with a result set of Item.ID which contains only those where Source.ID is 10.
I think you can just inner join the four tables together in a single query. This should be safe, because in order for a relationship to exist between a source and an item, the latter must be reachable via a key relationship.
SELECT
t1.ID AS source_id,
t4.*
FROM Source t1
INNER JOIN Resource t2
ON t1.ID = t2.SOURCE_FK
INNER JOIN Item_Base t3
ON t2.ID = t3.RESOURCE_FK
INNER JOIN Item t4
ON t3.ID = t4.BASE_FK
WHERE
t1.ID = 10
You need INNER JOINs in your query. Then it's possible in one simple query:
SELECT
i.ID
FROM
Source s
INNER JOIN Resource r
ON s.ID = r.SOURCE_FK
INNER JOIN Item_Base b
ON r.ID = b.RESOURCE_FK
INNER JOIN Item i
ON b.ID = i.BASE_FK
WHERE
s.ID = 10
See https://dev.mysql.com/doc/refman/5.7/en/join.html and http://www.mysqltutorial.org/mysql-inner-join.aspx for more info and examples relating to joins and how to use them.

Mysql select rows with same id's (3 tables)

I have the following tables:
'blog_content'
'blog_media'
'blog_media_content'
| blog_id | media_id |
========================
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 3 | 4 |
I want to select all blog_media.uri's where blog_media.media_id equals blog_media_content.blog_id.
Please help me to achieve my aim.
An inner join between blog_media and blog_media_content tables would suffice.
SELECT
bm.uri
FROM blog_media bm
INNER JOIN blog_media_content bmc ON bm.media_id = bmc.media_id
WHERE bmc.blog_id =3;
Note:
If you need any additional information from blog table then you need an additional inner join like below:
...INNER JOIN blog_table b ON bmc.blog_id = b.blog_id...
EDIT:
In order to get records for all blog_ids :
SELECT
bm.uri
FROM blog_media bm
INNER JOIN blog_media_content bmc ON bm.media_id = bmc.media_id
ORDER BY bmc.blog_id;

MySQL: Return row count from second table using one query

I have the following two tables. I need to select all the lightboxes from lightboxes_tbl where author ='scott#co.com'. That's obviously the easy part. Where I am stuck is that I also want to select in the same query the number of assets in each lightbox. For example, 'aircraft-types' lightbox (id = 100 / lightbox_id = 100) would return 2 assets. The 'maintenance' lightbox (id = 101 / lightbox_id = 101) would return 1 asset.
Thanks!
lightboxes_tbl
+-----+----------------+---------------+---------------------+
|id |lightbox_name |author |authoried_viewers |
+-----+----------------+---------------+---------------------+
|100 | aircraft-types |scott#co.com |jon#co.com,aj#co.com |
+-----+----------------+---------------+---------------------+
|101 | maintenance |scott#co.com |nicole#co.com |
+-----+----------------+---------------+---------------------+
lightbox_assets_tbl
+-----+-------------+-------------+---------------+----------+
|id |lightbox_id |asset_name |asset_path | asset_id |
+-----+-------------+-------------+---------------+----------+
|1 |100 |a321.jpg |project1/imgs/ | 3700 |
+-----+-------------+-------------+---------------+----------+
|2 |100 |b757.jpg |project1/imgs/ | 3444 |
+-----+-------------+-------------+---------------+----------+
|3 |101 |engine.jpg |project4/imgs/ | 1444 |
+-----+-------------+-------------+---------------+----------+
Make use of LEFT JOIN and COUNT()
SELECT l.*, COUNT(a.lightbox_id) total_assets
FROM lightboxes_tbl l LEFT JOIN lightbox_assets_tbl a
ON l.id = a.lightbox_id
WHERE l.author = 'scott#co.com'
GROUP BY l.id
Output:
| ID | LIGHTBOX_NAME | AUTHOR | AUTHORIED_VIEWERS | TOTAL_ASSETS |
|-----|----------------|--------------|----------------------|--------------|
| 100 | aircraft-types | scott#co.com | jon#co.com,aj#co.com | 2 |
| 101 | maintenance | scott#co.com | nicole#co.com | 1 |
Here is a SQLFiddle demo
Recommended reading:
A Visual Explanation of SQL Joins
Join to the assts table:
select
lb.id, lb.lightbox_name, lb.author, lb.authoried_viewers,
sum(a.id is not null) asset_count
from lightboxes_tbl
left join lightbox_asset_tbl a
on a.lightbox_id = lb.id
where author ='scott#co.com'
group by lb.id, lb.lightbox_name, lb.author, lb.authoried_viewers
There's a little trick in there: sum() is used to count how many rows are not null in the asst table, which will produce a total of zero for light boxes that have no assets - something that count() won't do when using a left join.
BTW, in mysql a boolean result is 1 if true, 0 if false, so summing a condition neatly counts how many times it was true.
You probably want something along the lines of this query...
SELECT LBT.id, LBT.lightbox_name, LBT.author, LBTA.id, LBTA.asset_name, LBTA.asset_path, LBTA.asset_id FROM lightboxes_tbl LBT JOIN lightbox_assets_tbl LBTA ON LBTA.lightbox_id = LBT.id WHERE author = 'scott#co.com'
azsl1326
this I write simple query for example, hope it helps,
select
a.id, a.lightbox_name, b.id, b.lightbox_id, b.asset_name, b.asset_id
from lightboxes_tbl a
join lightbox_assets_tbl b
on (a.id = b.lightbox_id)
where a.author = "scott#co.com"

When to use LEFT JOIN and when to use INNER JOIN?

I feel like I was always taught to use LEFT JOINs and I often see them mixed with INNERs to accomplish the same type of query throughout several pieces of code that are supposed to do the same thing on different pages. Here goes:
SELECT ac.reac, pt.pt_name, soc.soc_name, pt.pt_soc_code
FROM
AECounts ac
INNER JOIN 1_low_level_term llt on ac.reac = llt.llt_name
LEFT JOIN 1_pref_term pt ON llt.pt_code = pt.pt_code
LEFT JOIN 1_soc_term soc ON pt.pt_soc_code = soc.soc_code
LIMIT 100,10000
Thats one I am working on:
I see a lot like:
SELECT COUNT(DISTINCT p.`case`) as count
FROM FDA_CaseReports cr
INNER JOIN ae_indi i ON i.isr = cr.isr
LEFT JOIN ae_case_profile p ON cr.isr = p.isr
This seems like the LEFT may as well be INNER is there any catch?
Is there any catch? Yes there is -- left joins are a form of outer join, while inner joins are a form of, well, inner join.
Here's examples that show the difference. We'll start with the base data:
mysql> select * from j1;
+----+------------+
| id | thing |
+----+------------+
| 1 | hi |
| 2 | hello |
| 3 | guten tag |
| 4 | ciao |
| 5 | buongiorno |
+----+------------+
mysql> select * from j2;
+----+-----------+
| id | thing |
+----+-----------+
| 1 | bye |
| 3 | tschau |
| 4 | au revoir |
| 6 | so long |
| 7 | tschuessi |
+----+-----------+
And here we'll see the difference between an inner join and a left join:
mysql> select * from j1 inner join j2 on j1.id = j2.id;
+----+-----------+----+-----------+
| id | thing | id | thing |
+----+-----------+----+-----------+
| 1 | hi | 1 | bye |
| 3 | guten tag | 3 | tschau |
| 4 | ciao | 4 | au revoir |
+----+-----------+----+-----------+
Hmm, 3 rows.
mysql> select * from j1 left join j2 on j1.id = j2.id;
+----+------------+------+-----------+
| id | thing | id | thing |
+----+------------+------+-----------+
| 1 | hi | 1 | bye |
| 2 | hello | NULL | NULL |
| 3 | guten tag | 3 | tschau |
| 4 | ciao | 4 | au revoir |
| 5 | buongiorno | NULL | NULL |
+----+------------+------+-----------+
Wow, 5 rows! What happened?
Outer joins such as left join preserve rows that don't match -- so rows with id 2 and 5 are preserved by the left join query. The remaining columns are filled in with NULL.
In other words, left and inner joins are not interchangeable.
Here's a rough answer, that is sort of how I think about joins. Hoping this will be more helpful than a very precise answer due to the aforementioned math issues... ;-)
Inner joins narrow down the set of rows returns. Outer joins (left or right) don't change number of rows returned, but just "pick up" additional columns if possible.
In your first example, the result will be rows from AECounts that match the conditions specified to the 1_low_level_term table. Then for those rows, it tries to join to 1_pref_term and 1_soc_term. But if there's no match, the rows remain and the joined in columns are null.
An INNER JOIN will only return the rows where there are matching values in both tables, whereas a LEFT JOIN will return ALL the rows from the LEFT table even if there is no matching row in the RIGHT table
A quick example
TableA
ID Value
1 TableA.Value1
2 TableA.Value2
3 TableA.Value3
TableB
ID Value
2 TableB.ValueB
3 TableB.ValueC
An INNER JOIN produces:
SELECT a.ID,a.Value,b.ID,b.Value
FROM TableA a INNER JOIN TableB b ON b.ID = a.ID
a.ID a.Value b.ID b.Value
2 TableA.Value2 2 TableB.ValueB
3 TableA.Value3 3 TableB.ValueC
A LEFT JOIN produces:
SELECT a.ID,a.Value,b.ID,b.Value
FROM TableA a LEFT JOIN TableB b ON b.ID = a.ID
a.ID a.Value b.ID b.Value
1 TableA.Value1 NULL NULL
2 TableA.Value2 2 TableB.ValueB
3 TableA.Value3 3 TableB.ValueC
As you can see, the LEFT JOIN includes the row from TableA where ID = 1 even though there's no matching row in TableB where ID = 1, whereas the INNER JOIN excludes the row specifically because there's no matching row in TableB
HTH
Use an inner join when you want only the results that appear in both tables that matches the Join condition.
Use a left join when you want all the results from Table A, but if Table B has data relevant to some of Table A's records, then you also want to use that data in the same query.
Use a full join when you want all the results from both Tables.
For newbies, because it helped me when I was one: an INNER JOIN is always a subset of a LEFT or RIGHT JOIN, and all of these are always subsets of a FULL JOIN. It helped me understand the basic idea.