INNER JOIN unique column mysql - php

I have planned to use three tables for my sites tagging system, they looks like:
|-----------------------|
|==> photo |
| -> id |
| -> custom_id |
| -> title |
| -> slug |
| -> date |
| -> image_url |
|-----------------------|
|==> tags |
| -> id |
| -> slug |
| -> title |
|-----------------------|
|==> tags_relation |
| -> tid | <-- this is the tags.id
| -> pid | <-- this is the photo.custom_id
|-----------------------|
So, to fetch the recent posts of a specific tag i am using INNER JOIN by the following query:
SELECT p.id, p.custom_id, p.title, p.slug, p.date, p.image_url, t.id, t.slug, tr.*
FROM tags t INNER JOIN
tags_relation tr
ON t.id = tr.tid INNER JOIN
photo p
ON p.custom_id = tr.pid
WHERE t.slug = 'people'
ORDER BY p.date DESC
LIMIT 20
Everything works fine except the fact that the 'slug, id, title' column of the photo table is being replaced by the 'slug, id, title' column of the tags table!
I figured out a solution that is changing the tags columns name, but is there any best practices to solve this problem?
Thanks

I think that you should use Aliases.
For example:
SELECT p.id AS Person_Id, p.title AS Person_Title ...
You can learn more about aliases here.

You're going to have to cast some of those fields like this
SELECT t.column, s.column AS column2
Otherwise, MySQL will indeed pick a field to return (typically the last one with that name) and that's all you get!

Our best practice may seem like more work, but we do add unique prefixes to all our column names so you don't get lost in an alias war.
|-----------------------|
|==> photo |
| -> photo_id |
| -> photo_custom_id |
| -> photo_title |
| -> photo_slug |
| -> photo_date |
| -> photo_image_url |
|-----------------------|
|==> tags |
| -> tag_id |
| -> tag_slug |
| -> tag_title |
|-----------------------|
|==> tags_relation |
| -> tagRelation_tid |
| -> tagRelation_pid |
|-----------------------|
Which would change your query to
SELECT photo_id, photo_custom_id, photo_title, photo_slug, photo_date, photo_image_url, tag_id, tag_slug, tags_relation.*
FROM tags INNER JOIN
tags_relation
ON tag_id = tagRelation_tid INNER JOIN
photo
ON photo_custom_id = tagRelation_pid
WHERE tag_slug = 'people'
ORDER BY photo_date DESC
LIMIT 20
More verbose, but also more descriptive and when you have many many tables and really really long joins, it works out great...especially when your intellisense kicks in.

Related

MySQL - inner join - add column with value based on other value

I'm struggling with mysql joins :/
I've multiple tables inside database fe. tasks, users etc.
Table tasks containing tasks with various variables, but the most important - id's of users signed to task (as different roles inside the task - author, graphic, corrector):
+---------+-------------+--------------+
| task_id | task_author | task_graphic |
+---------+-------------+--------------+
| 444 | 1 | 2 |
+---------+-------------+--------------+
Table users
+---------+----------------+------------+-----------+
| user_id | user_nice_name | user_login | user_role |
+---------+----------------+------------+-----------+
| 1 | Nice Name #1 | login1 | 0 |
+---------+----------------+------------+-----------+
| 2 | Bad Name #2 | login2 | 1 |
+---------+----------------+------------+-----------+
Using PDO I'm getting the whole data I want while using INNER JOIN with data from different tables (and $_GET variable)
SELECT tasks.*, types.types_name, warehouse.warehouse_id, warehouse.warehouse_code, warehouse.warehouse_description
FROM tasks
INNER JOIN types ON types.types_id = tasks.task_id
INNER JOIN warehouse ON warehouse.warehouse_id = tasks.task_id
WHERE tasks.task_id = '".$get_id."'
ORDER BY tasks.task_id
Above query returns:
+---------+--------------+--------------+----------------+------------+-----------+------------------+------------------------+------------+-------------+-----------------+-----------+----------------+--------------------+---------------------+-----------+---------------------+------------------+---------------------+
| task_id | task_creator | task_graphic | task_purchaser | task_title | task_lang | task_description | task_description_files | task_files | task_status | task_prod_index | task_type | task_print_run | task_print_company | task_warehouse_code | task_cost | task_time_added | task_deadline | task_date_warehouse |
+---------+--------------+--------------+----------------+------------+-----------+------------------+------------------------+------------+-------------+-----------------+-----------+----------------+--------------------+---------------------+-----------+---------------------+------------------+---------------------+
| 2 | 1 | 2 | 1 | Test | PL | Lorem ipsum (?) | | | w | 2222 | 3 | 456546 | Firma XYZ | 2 | 124 | 29.09.2016 15:48:20 | 01.10.2016 12:00 | 07.10.2016 14:00 |
+---------+--------------+--------------+----------------+------------+-----------+------------------+------------------------+------------+-------------+-----------------+-----------+----------------+--------------------+---------------------+-----------+---------------------+------------------+---------------------+
And I'd like to get query with added user_nice_name after task_creator, task_author and task_graphic - obviously nice names selected from table users based on ID's provide in 3 above fields fe.
+---------+--------------+------------------------------------+--------------+--------------------------------------+
| task_id | task_creator | task_creator_nn | task_graphic | task_graphic |
+---------+--------------+------------------------------------+--------------+--------------------------------------+
| 2 | 1 | Nice Name (from task_creator ID=1) | 2 | Nice Name (from task_graphic ID = 2) |
+---------+--------------+------------------------------------+--------------+--------------------------------------+
How can I achieve that?
You need three joins:
SELECT t.*,
uc.user_nice_name as creator_name,
ug.user_nice_name as graphic_name,
up.user_nice_name as purchaser_name,
ty.types_name, w.warehouse_id, w.warehouse_code, w.warehouse_description
FROM tasks t INNER JOIN
types ty
ON ty.types_id = t.task_id INNER JOIN
warehouse w
ON w.warehouse_id = t.task_id LEFT JOIN
users uc
ON uc.user_id = t.task_creator LEFT JOIN
users ug
ON ug.user_id = t.task_graphic LEFT JOIN
users up
ON up.user_id = t.task_purchaser
WHERE t.task_id = '".$get_id."'
ORDER BY t.task_id;
Notes:
Table aliases make the query easier to write and to read. They are also required because you have three references to users in the FROM clause.
This uses LEFT JOIN for the users in case some of the reference values are missing.
You need to work on your naming. It doesn't make sense that a "warehouse" id matches a "task" id. Or that a "task" id matches a "types" id. But that is how you phrased the query in your question.
The ORDER BY effectively does nothing, because all rows have the same task_id.
Assuming that the task_graphic_name is inside a table name task_graphic_table and the relation field are task_graphic_id
SELECT tasks.*
, types.types_name
, warehouse.warehouse_id
, warehouse.warehouse_code
, warehouse.warehouse_description
, users.user_nice_name
FROM tasks
INNER JOIN types ON types.types_id = tasks.task_id
INNER JOIN warehouse ON warehouse.warehouse_id = tasks.task_id
INNER JOIN users ON users.user_nice_name = tasks.task_graphic
WHERE tasks.task_id = '".$get_id."'
ORDER BY tasks.task_id
And if you need the column appear in a specific order you should explicitally call the column name in sequence eg:
SELECT tasks.col1
, task.col2
, types.types_name
, warehouse.warehouse_id
, warehouse.warehouse_code
, task.col2
, warehouse.warehouse_description
, task_graphic_table.task_graphic_name
Add two sub query in with your query. like
SELECT tasks.*,
....
....,
(select user_nice_name from users where id = tasks.task_author) AS task_creator_name,
(select user_nice_name from users where id = tasks.task_graphic) AS task_graphic_name
FROM tasks
INNER JOIN types ON types.types_id = tasks.task_id
....
....

mysql select where left join syntax

I have a problem. I have 2 database tables.
table 1 people:
+----------+--------------+
| id | name |
+----------+--------------+
| 1 | johanalj |
| 2 | hjgjhggjh |
+----------+--------------+
table 2 images of people:
+----------+--------------+----------------+
| id | url | people_ID |
+----------+--------------+----------------+
| 1 | 3765345.png | 1 |
| 2 | 87e58974.png | 1 |
+----------+--------------+----------------+
Now I want to select person with id 1 from table 1 and all pictures from table 2 that have people_ID 1.
I tried LEFT JOIN in combination with a WHERE but cant get it to work
$sql = "SELECT * FROM people p LEFT JOIN images i ON i.people_ID = p.id WHERE id = '1'";
But I get a no result massage. What am I doing wrong?
There is an error(ambiguous column id). Both tables have id column. You need to add the table alias with id. try with -
$sql = "SELECT * FROM people p LEFT JOIN images i ON i.people_ID = p.id WHERE p.id = '1'";

mysql call tables mp3s,artists,tags

mp3s
ID
Title
Description
tags
ID
Title
artists
ID
Title
artist_relations
mp3ID //call to mp3s.ID
artistID // call to artists.ID
tag_relations
mp3ID //call to mp3s.ID
tagID // call to tags.ID
I need this result in one query:
row1 : mp3ID | mp3Title | mp3Description | tag1,tag2,tag3 | artist1
row2 : mp3ID | mp3Title | mp3Description | tag20,tag22 | artist8,artist5
...
select
mp3.ID as mp3id,
mp3.title as mp3title,
mp3.description as mp3description,
group_concat(distinct artist.title) as artists,
group_concat(distinct tag.title) as tags
from mp3s as mp3
left join artist_relations as ar
on ar.MP3ID = mp3.ID
left join artists artist
on ar.ARTISTID = artist.ID
left join tag_relations as tr
on tr.MP3ID = mp3.ID
left join tags as tag
on tag.ID = tr.TAGID
group by mp3.ID
And I tried to reproduce on expecially created db with structure you described and got this result:
| mp3_id | mp3_title | mp3_description | artists | tags |
| 1 | first | first one | zemfira,splean | a,b |
| 2 | second | second one | leningrad,deep purple,led zeppelin | c,d,e |

Comparing values of columns from 3 tables - MySQL

following question:
I'm working with 3 tables = actors, movies, roles. I'm trying to find all the movies a given actor, say 'Robin Williams' has been in by comparing the specific actor id since there be may more than one actor with the same name. The actors table has the following relevant columns: first_name, last_name, id - the movies table has columns: id (the movie's id) - and the roles table has: actor_id and movie_id.
Do I JOIN the tables or use UNION? How do I compare columns from different tables when the columns have different names?
Thank you!
Just for reference:
Table actors:
mysql> SELECT *
-> FROM actors;
+--------+--------------------+------------------------+--------+------------+
| id | first_name | last_name | gender | film_count |
+--------+--------------------+------------------------+--------+------------+
| 933 | Lewis | Abernathy | M | 1 |
| 2547 | Andrew | Adamson | M | 1 |
| 2700 | William | Addy | M | 1 |
Table movies:
mysql> SELECT *
-> FROM movies;
+--------+------------------------------+------+------+
| id | name | year | rank |
+--------+------------------------------+------+------+
| 10920 | Aliens | 1986 | 8.2 |
| 17173 | Animal House | 1978 | 7.5 |
| 18979 | Apollo 13 | 1995 | 7.5 |
Table roles:
mysql> SELECT *
-> FROM roles;
+----------+----------+-------------------------------+
| actor_id | movie_id | role |
+----------+----------+-------------------------------+
| 16844 | 10920 | Lydecker |
| 36641 | 10920 | Russ Jorden |
| 42278 | 10920 | Cpl. Dwayne Hicks |
At first I tried setting each check equal to a PHP variable and comparing them but that seemed wrong, then I tried:
mysql> SELECT roles.actor_id, roles.movie_id, movies.id, actors.id
-> FROM roles
-> JOIN movies, actors
-> ON roles.actor_id = actors.id && roles.movie_id =movies.id;
which again does not work.
Finally figured it out..
>SELECT m.name, m.year
-> FROM movies m
->JOIN roles r ON m.id = r.movie_id
->JOIN actors a ON a.id = r.actor_id
->WHERE a.first_name = "whatever actor's first name"
->AND a.last_name = "whatever actor's last name"
This will then give you two columns with the corresponding name and year! hazzah!
First Read this answer - it made it click for me finally after years of unions when should join and vice versa.
In this case you should definitely JOIN as you want the result to act as a single row.
(think of it like this - I want to see Movie, Actor -> together as one result)
PS
You don't need your film count field any more as once you have the joins worked out you can just use MySQL COUNT -> it will make it easier to maintain.
You need a join. Try this:
SELECT A.first_name,A.last_name,A.gender,M.name,M.year,M.rank,R.role
FROM roles R INNER JOIN
Movies M ON R.movie_id = M.movie_id INNER JOIN
Actors A ON R.actory_id = A.id

DISTINCT with an inner join

I'm currently working on a website for a photographer, every photo is tagged with multiple keywords which are extracted when uploading.
My database looks like this (simplified)
TAGS
-------------------
| | |
| | |
| ID | TAG |
| | |
| | |
-------------------
IMAGES
-------------------
| | |
| | |
| ID | URL |
| | |
| | |
-------------------
TAGS_IMAGES
-------------------
| | |
| TAG | IMAGE |
| ID | ID |
| | |
| | |
-------------------
So all tags are stored in a seperated table to prevent duplicates, the same goes for the images and then the tags and images are linked together in another table.
When searching for a tag, I execute this SQL to find all images based on the given tag
SELECT DISTINCT SQL_CALC_FOUND_ROWS tags.tag, tags_image.imageID, images.src FROM tags INNER JOIN tags_image ON tags.tagID = tags_image.tagID INNER JOIN images ON tags_image.imageID = images.id WHERE tag LIKE ? ORDER BY id DESC LIMIT ?, ?
But the problem is that I'm still getting multiple duplicates because the DISTINCT only seems to work on the tag.id.
As you can see here: http://pastebin.com/MWt5B0Aq, based on the tag "water", some images have keywords like "water", "watervogel", "waterloop".
Is there a way to get the DISTINCT to work on the images.id?
I tried this, but that didn't help
SELECT DISTINCT images.id SQL_CALC_FOUND_ROWS tags.tag, tags_image.imageID, images.src FROM tags INNER JOIN tags_image ON tags.tagID = tags_image.tagID INNER JOIN images ON tags_image.imageID = images.id WHERE tag LIKE ? ORDER BY id DESC LIMIT ?, ?
Would doing a group by help?
SELECT DISTINCT SQL_CALC_FOUND_ROWS tags.tag, tags_image.imageID, images.src FROM tags INNER JOIN tags_image ON tags.tagID = tags_image.tagID INNER JOIN images ON tags_image.imageID = images.id WHERE tag LIKE ? GROUP BY images.id ORDER BY id DESC LIMIT ?, ?

Categories