I have three tables.They are
tb_albums---->id,title, description
tb_photos---->id,album_id, photo
tb_tags---->id,album_id, tag
Here i want to get the albums details and it photos & it tags through tb_albums.id.
How to use join query here?
Normally, you can use as many tables in JOIN, as you'd like. Just add another JOIN statement. You can refer to k102's answer for the correct syntax (which doesn't produce correct result though).
But in this particular case you don't want to use simple JOIN on all tables, unless you have only one photo per album and only one tag per album. If you have more than one photo per and more than one tag per album, JOIN both tables on album_id in single query will produce Cartesian product of both, in other words all possible combinations of tags and photos from each albums. For N photos and M tags that's N * M results, instead of N + M.
Also, there is no point of joining with tb_albums, as you do not need to repeat information about each album for each photo and each tag.
Proper approach would be to have 3 separate simple SELECTs from each table and combining their result on application level.
If for some awkward reason you'd need to do that with one query, you can do something like:
SELECT * FROM tb_albums as A JOIN
(SELECT 'photo', id, photo as value FROM tb_photos
UNION ALL
SELECT 'tag', id, tag as value FROM tb_tags) as B ON B.album_id = A.id
Note, this is way less optimal than separate SELECTs, you should only do this if you have no other choice.
select * from tb_albums a
join tb_photos p on a.id = p.album_id
join tb_tags t on a.id = t.album_id
Related
Right now I am using three queries to get the rows that I need:
The main information from pins:
SELECT * FROM pins
WHERE lat > 45
The two columns from pins_tags that associate pins with one or more tags:
SELECT pins_tags.pin_id, pins_tags.tag_id FROM pins_tags
INNER JOIN pins
ON pins_tags.pin_id = pins.id
WHERE pins.lat > 45
And finally the information about the tags associated with the pins I just selected:
SELECT tags.id, tags.caption FROM tags
INNER JOIN pins_tags
ON pins_tags.tag_id = tags.id
INNER JOIN pins
ON pins_tags.pin_id = pins.id
WHERE pins.lat > 45
You can see I have to use the same WHERE clause in each query to get the tags associated specifically with the pins I returned in the first query. This works fine and returns exactly the three arrays that I want. But I feel like I'm not seeing a more efficient way to do this. Is there one?
What I understood: Select all tags and the corresponding "details" from pins_tags and pins. You can choose yourself which columns should be fetched.
SELECT
tags.*
pins_tags.*,
pins.*
FROM tags
LEFT JOIN pins_tags ON pins_tags.tag_id = tags.id
LEFT JOIN pins ON pins.id = pins_tags.pin_id
WHERE pins.lat > 45
A note: The question text comparing with the title is very confusing. Please read it yourself again, but from the perspective of the user who doesn't know why you use 3 statements (arrays ?! - probably has something to do with what you've applied in PHP?) and what exactly you want to achieve :-)
Good luck.
I have a table of albums, each album has an ID.
I also have a table of reviews, each review is linked to a corresponding album through the column albumID.
I'm wondering how I can select all albums who's ID's are present in the reviews table.
I was thinking of selecting all albumId's from the reviews column then doing a select where id in but I feel like this would be horribly inefficient.
This is in MySQL
Albums Table
- ID
Reviews Table
- ID
- albumID
Desired result: All albums who have a review. (eg: All albums that have their ID present in the Reviews table)
Here's one approach using a simple JOIN. The inner join notation will only include records that exist in both tables. It allows you to access data in both tables which may be useful if you need data out of reviews too.
Select * from albums A
inner join reviews R
on A.ID = R.AlbumID
This next approach is generally the fastest but you can only get data from albums. This uses what's known as a correlated sub query and is generally the fastest approach.
SELECT * from albums A
where exists (Select 1 from reviews R where A.ID = R.albumID)
and a third approach but generally the slowest... uses a concept called IN(). The sub query here generates a list of ID's from reviews and only shows albums in that list.
Select * from albums where ID IN (SELECT albumID from Reviews)
This would work:
select a.* from albums as a
inner join reviews as r
on r.albumID = a.ID
Imagine a table for articles. In addition to the main query:
SELECT * From articles WHERE article_id='$id'
We also need several other queries to get
SELECT * FROM users WHERE user_id='$author_id' // Taken from main query
SELECT tags.tag
FROM tags
INNER JOIN tag_map
ON tags.tag_id=tag_map.tag_id
WHERE article_id='$id'
and several more queries for categories, similar articles, etc
Question 1: Is it the best way to perform these queries separately with PHP and handle the given results, or there is way to combine them?
Question 2: In the absence of many-to-many relationships (e.g. one tag, category, author for every article identified by tag_id, category_id, author_id); What the best (fastest) was to retrieve data from the tables.
If all the relationships are one-many then you could quite easily retrieve all this data in one query such as
SELECT
[fields required]
FROM
articles a
INNER JOIN
users u ON a.author_id=u.user_id
INNER JOIN
tag_map tm ON tm.article_id=a.article_id
INNER JOIN
tags t t.tag_id=tm.tag_id
WHERE
a.article_id='$id'
This would usually be faster than the three queries separately along as your tables are indexed correctly as MySQL is built to do this! It would save on two round trips to the database and the associated overhead.
You can merge in the user in the first query:
SELECT a.*, u.*
FROM articles a
JOIN users u ON u.user_id = a.author_id
WHERE a.article_id='$id';
You could do the same with the tags, but that would introduce some redundancy in the answer, because there are obviously multiple tags per article. May or may not be beneficial.
In the absence of many-to-many relationships, this would do the job in one fell swoop and would be superior in any case:
SELECT *
FROM users u
JOIN articles a ON a.author_id = u.user_id
JOIN tag t USING (tag_id) -- I assume a column articles.tag_id in this case
WHERE a.article_id = '$id';
You may want to be more selective on which columns to return. If tags ar not guaranteed to exist, make the second JOIN a LEFT JOIN.
You could add an appropriately denormalized view over your normalized tables where each record contains all the data you need. Or you could encapsulate the SQL calls in stored procedures and call these procs from your code, which should aid performance. Prove both out and get the hard figures; always better to make decisions based on evidence rather that ideas. :)
I have two table for gallery system :
gallery_cat(
gallery_cat_id PK,
gallery_cat_name
)
gallery(
gallery_id PK,
gallery_cat_id FK,
gallery_name,
gallery_file_name,
gallery_date
)
I need to write a SQL query that return one picture from gallery table for each album, the purpose of this that I need to list the albums with one picture for each.
gallery_name | gallery_cat_name| gallery_file_name
-------------+-----------------+------------------
pic1 | Album1 | pic1.jpg
This should do the trick:
SELECT g2.gallery_name, gc2.gallery_cat_name, g2.gallery_file_name
FROM gallery g2
INNER JOIN gallery_cat gc2 ON (g2.gallery_cat_id = gc2.gallery_cat_id)
WHERE g2.gallery_id IN (
SELECT g.gallery_id
FROM gallery g
GROUP BY g.gallery_cat_id)
Explanation:
At the end is a sub-select
IN (
SELECT g.gallery_id
FROM gallery g
GROUP BY g.gallery_cat_id) <<-- select 1 random g.id per gallery_cat.
Here I select all g.id, but because of the group by clause it will reduce the results to 1 row per grouped by item. I.e. 1 row (chosen more or less at random) per g.gallery_cat_id.
Next I do a normal select with a join:
SELECT g2.gallery_name, gc2.gallery_cat_name, g2.gallery_file_name
FROM gallery g2
INNER JOIN gallery_cat gc2 ON (g2.gallery_cat_id = gc2.gallery_cat_id)
WHERE g2.gallery_id IN (
Because I refer to the same table twice in the same query you have to use an alias(*).
I select all names and all catnames and all filenames.
However in the where clause I filter these so that only rows from the sub-select are shown.
I have to do it this way, because the group by mixes rows into one messed up ow, if I select from that directly I will get values from different rows mixed together, not a good thing.
By first selecting the id's I want and then matching full rows to those id I prevent this from happening.
*(in this case with this kind of subselect that's not really 100% true, but trust me on the point that it's always a good idea to alias your tables)
This attempts to select the most recent gallery_date for each category ID and join against gallery_cat
SELECT
c.gallery_cat_id,
c.gallery_cat_name,
i.lastimg
FROM
gallery_cat c
LEFT JOIN (
SELECT gallery_cat_id, gallery_filename AS lastimg, MAX(gallery_date)
FROM gallery
GROUP BY gallery_cat_id, gallery_filename
) i ON c.gallery_cat_id = i.gallery_cat_id
You can use SQL JOINS to do this, otherwise you would have to loop out all the albums and pick one random picture from each which would be less efficient.
i have many to many database and i'm there is a query that i just got stuck with and cant do it.
i have 4 tables Artists, Tracks, Albums, Clips
now i'm on the artist page so i need to get them by the artist page, i already got all of them, but not the way i want them.
because some tracks, albums, clips belong to other artists as well (duet) and i need to display their name.
but the problem is that i'm selecting using the artist id so my GROUPC_CONCAT function wont work here is the query that gets the artist albums.
SELECT al.album_name, GROUP_CONCAT(a.artist_name SEPARATOR ', ') AS 'artist_name'
FROM
Albums al
LEFT JOIN
ArtistAlbums art ON art.album_id = al.album_id
LEFT JOIN
Artists a on a.artist_id = art.artist_id
WHERE
a.artist_id = 10
GROUP BY
al.album_id
one of the albums have two artists attached to it, but it does not get the other artist name.
when i select by the album_id i get the two artists.
please note that i'm new to mysql and i did not find any answers on this particular problem almost no resources on many-to-many querying.
how can i tackle this problem.?
any resources or books on many-to-many that show how to deal with the database on the application layer will be much appreciated,
thanks in advance.
Think of table aliases as really being row aliases. That is, for purposes of expressions in the WHERE clause and the select-list, the alias refers to a single row at a time.
Since you've created a condition such that a.artist_id = 10, then only rows for that artist match the condition. What you really want is to match all artists on an album given that one artist is artist_id = 10.
For that, you need another join, so that the row where artist_id = 10 is matched to all the rows for that respective album.
SELECT al.album_name, GROUP_CONCAT(a2.artist_name SEPARATOR ', ') AS `artist_name`
FROM
Albums al
INNER JOIN
ArtistAlbums art ON art.album_id = al.album_id
INNER JOIN
Artists a on a.artist_id = art.artist_id
INNER JOIN
ArtistAlbums art2 ON art2.album_id = al.album_id
INNER JOIN
Artists a2 on a2.artist_id = art2.artist_id
WHERE
a.artist_id = 10
GROUP BY
al.album_id
P.S.: I've also replaced your use of LEFT JOIN with INNER JOIN. There's no reason you needed LEFT JOIN, since you're explicitly looking for albums that have a matching artist, not all albums whether or not it has artist 10. Review the meaning of different types of join.
Re your followup question:
I don't know of a book specifically about many-to-many queries. I'd recommend books like:
SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming, which is my own book and it does cover many-to-many tables.
SQL and Relational Theory to understand joins better.
Joe Celko's SQL Programming Style, which imho is Joe Celko's best book.