Wednesday, September 15, 2010

MySQL part 2: a bit more advanced JOINs.

Last time I explained the very basics of  JOINs. Now I will go a bit more further and show how to build more advanced queries. (I will continue on the same database.)

First let’s remind the clauses order in the SELECT query:

SELECT

FROM table_references

WHERE where_condition]

[GROUP BY {col_name | expr | position}

[ASC | DESC], ... [WITH ROLLUP]]

[HAVING where_condition]

[ORDER BY {col_name | expr | position}

[ASC | DESC], ...]

[LIMIT {[offset,] row_count | row_count OFFSET offset}]

WHERE indicates the condition or conditions that rows must satisfy to be selected. GROUP BY must be used with any aggregating functions to group the resultset by one or more columns. HAVING works only with GROUP BY and should be used only with aggregation functions (while the WHERE clause cannot). ORDER BY is used to order the result set by a desired column or columns.

Many-To-Many

Let’s choose all pictures with a list of people tagged on each of them. This is a many-to-many relation that requires two joins.

select filename, name from people_pictures join pictures on (people_pictures.picture_id = pictures.id) join people on (people_pictures.people_id=people.id);

picture1.jpg Jack
picture1.jpg Ben
picture1.jpg Claire
picture2.jpg John
picture2.jpg Jack
picture2.jpg Ben
picture3.jpg Jin
picture3.jpg Jack
picture5.jpg John
picture5.jpg Sun

The result set has many rows with the same filenames. Sometimes this can be a disadvantage. There is an easy way to show every picture with coma-separated list of tagged characters. For that we can use group_concat function with GROUP BY clause:

select filename, group_concat(name) from people_pictures join pictures on (people_pictures.picture_id = pictures.id) join people on (people_pictures.people_id=people.id) group by filename;

picture1.jpg Jack,Claire,Ben
picture2.jpg Ben,Jack,John
picture3.jpg Jack,Jin
picture5.jpg Sun,John

If we need to exclude some results on some conditions we can use HAVING clause. Let’s choose only the pictures with at least three people tagged on them.

select filename from people_pictures join pictures on (people_pictures.picture_id = pictures.id) join people on (people_pictures.people_id=people.id) group by filename having count(name)>2;

picture1.jpg
picture2.jpg

We can also find all pictures with Jack.

select filename from people_pictures join people on (people_pictures.people_id = people.id) join pictures on (people_pictures.picture_id=pictures.id) where name = 'Jack';

picture1.jpg
picture2.jpg
picture3.jpg

At the end let’s find the most tagged character.

select people.name from people_pictures join people on (people_pictures.people_id = people.id) group by people_id order by count(people_id) desc limit 1;

Jack

All above examples work just fine for small database. However the loading time grows with the number of records. Next time I will show how to optimize such queries to keep the loading time reasonable .

No comments:

Post a Comment