MySQL displaying results from a many-to-many relationship - php

I have set up a many-to-many relationship db with 3 tables:
CREATE TABLES Films (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
Title VARCHAR(255)),
CREATE TABLE Moods (
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
mood_name VARCHAR(255))
CREATE TABLE Films_Moods (
film_id INT NOT NULL,
mood_id INT NOT NULL,
PRIMARY KEY (film_id, mood_id),
FOREIGN KEY (film_id) REFERENCES Films(id) ON UPDATE CASCADE,
FOREIGN KEY (mood_id) REFERENCES Moods(id) ON UPDATE CASCADE)";
So there might be a film with 2 or more moods. Let them be $mood1 and $mood2.
I want to display the selected film in a table as a single row, for example in the following way:
<table>
<tr>
<th>Title</th>
<th>Mood(s)</th>
</tr>
and the PHP code:
while($row = mysqli_fetch_array($result))
{
echo "<tr>";
echo "<td>" . $row['Title'] . "</td>";
echo "<td>" . $row['Moods'] . "</td>";
echo "</tr>";
}
The question is: how to obtain $row['Moods'] that would somehow combine two rows:
film_1 moodName_1
film_1 moodName_2
Or is there an alternative approach to what I am trying to achieve?
EDIT
Actually Im using tables Genres and Ambiences similarly to Moods and Im trying to do a search with specific Genres and Ambiences:
"SELECT *,GROUP_CONCAT(ambienceName SEPARATOR ' ') AS ambiences FROM Films AS f
INNER JOIN Films_Genres AS fg ON f.id = fg.film_id
INNER JOIN Genres AS g ON g.id = fg.genre_id
INNER JOIN Films_Ambiences as fa ON f.id = fa.film_id
INNER JOIN Ambiences AS a ON a.id = fa.ambience_id
WHERE g.Name LIKE '$genre' AND (a.ambienceName LIKE '$ambience1' OR a.ambienceName LIKE '$ambience2')"

SELECT title,GROUP_CONCAT(mood_name SEPARATOR ' ') AS moods
FROM films
JOIN films_moods ON films.id=films_moods.film_id
JOIN moods ON films_moods.mood_id=moods.id
GROUP BY title
You can fiddle with this by going to http://sqlfiddle.com/#!2/c6dd3/14/0 - I also fixed a couple of typos in your schema.

Related

Multiple JOINs to the same subquery expression

I've recently been teaching myself SQL, and have been working on a toy project to do so. Here is a sample schema:
CREATE TABLE user (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
name VARCHAR(50)
);
INSERT INTO user(name) VALUES
("User 1"),
("User 2"),
("User 3"),
("User 4"),
("User 5");
CREATE TABLE friendship (
uid_1 INT,
uid_2 INT,
accepted_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (uid_1, uid_2),
CONSTRAINT fk_uid_1 FOREIGN KEY (uid_1) REFERENCES user (id),
CONSTRAINT fk_uid_2 FOREIGN KEY (uid_2) REFERENCES user (id)
);
INSERT INTO friendship(uid_1, uid_2) VALUES
(1, 2),
(2, 1);
CREATE TABLE event (
id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
name VARCHAR(50),
owner_id INT,
CONSTRAINT fk_owner_id FOREIGN KEY (owner_id) REFERENCES user (id)
);
INSERT INTO event (name, owner_id) VALUES
("Event 1", 1),
("Event 2", 2),
("Event 3", 3),
("Event 4", 4),
("Event 5", 5),
("Event 6", 1);
CREATE TABLE invite (
event_id INT NOT NULL,
sent_from_id INT NOT NULL,
sent_to_id INT NOT NULL,
PRIMARY KEY (event_id, sent_to_id),
CONSTRAINT fk_event_id FOREIGN KEY (event_id) REFERENCES event (id),
CONSTRAINT fk_sent_from_id FOREIGN KEY (sent_from_id) REFERENCES user (id),
CONSTRAINT fk_sent_to_id FOREIGN KEY (sent_to_id) REFERENCES user (id)
);
INSERT INTO invite(event_id, sent_from_id, sent_to_id) VALUES
(1, 2, 3);
As part of this project, I have a query that gets a list of users, with information populated relative to the currently authenticated user.
A simplified version of the query looks like this:
$select_users_query = "
SELECT
user.id AS id,
user.name AS name,
friendship.accepted_time AS friend_since
FROM user
LEFT JOIN friendship
ON friendship.uid_1 = user.id AND friendship.uid_2 = $relative_to_id
";
Then, at some endpoints, I want to return objects which have one or more users as sub-objects. In order to do this, I've been JOINing tables to the above query as a subquery, but when the returned object has multiple users (e.g., an invite to an event has a sending user, a receiving user, and a user that owns the event in question), the resulting query can end up pretty repetitive:
$select_invites_query = "
SELECT
event.id AS event_id,
event.name AS event_name,
owner.id AS owner_id,
owner.name AS owner_name,
owner.friend_since AS owner_friend_since,
sent_to.id AS sent_to_id,
sent_to.name AS sent_to_name,
sent_to.friend_since AS sent_to_friend_since,
sent_from.id AS sent_from_id,
sent_from.name AS sent_from_name,
sent_from.friend_since AS sent_from_friend_since,
FROM invite
INNER JOIN event
ON event.id = invite.event_id
INNER JOIN ($select_users_query) owner
ON event.owner_id = owner.id
INNER JOIN ($select_users_query) sent_from
ON invite.sent_from_id = sent_from.id
INNER JOIN ($select_users_query) sent_to
ON invite.sent_to_id = sent_to.id
";
My questions are:
Is repeating a subquery like this a performance issue during execution, assuming that the INNER JOINs all match on just a single row?
If not, is the additional parsing required for $select_invites_query a significant concern at all (especially as $select_users_query grows big and complex)?
Would using a variable here be a good idea, or a bad idea? From my inspection of EXPLAIN it seems as though MySQL is able to handle these JOINs pretty efficiently, but would defining a variable force MySQL to pull the unfiltered result set into memory before JOINing?
See SQL Fiddle schema here.
Since you appear to need self joins of same query, consider a Common Table Expression (CTE) (available in MySQL 8.0+) and with PHP parameterization. Below demonstrates with PHP's mysqli API in object-oriented and procedural styles:
$select_invites_query = "
WITH sub AS (
SELECT u.id AS id, u.`name` AS `name`,
f.accepted_time AS friend_since
FROM user
LEFT JOIN friendship f
ON f.uid_1 = u.id AND f.uid_2 = ?
)
SELECT event.id AS event_id, event.`name` AS event_name,
owner.id AS owner_id, owner.`name` AS owner_name,
owner.friend_since AS owner_friend_since,
sent_to.id AS sent_to_id, sent_to.`name` AS sent_to_name,
sent_to.friend_since AS sent_to_friend_since,
sent_from.id AS sent_from_id, sent_from.`name` AS sent_from_name,
sent_from.friend_since AS sent_from_friend_since
FROM invite
INNER JOIN event
ON event.id = invite.event_id
INNER JOIN sub owner
ON event.owner_id = owner.id
INNER JOIN sub sent_from
ON invite.sent_from_id = sent_from.id
INNER JOIN sub sent_to
ON invite.sent_to_id = sent_to.id";
// OBJECT-ORIENTED STYLE
$conn = new mysqli("my_host", "my_user", "my_pwd", "my_db");
$stmt = $conn->prepare($select_invites_query))
$stmt->bind_param("i", $relative_to_id);
$stmt->execute();
...
// PROCEDURAL STYLE
$conn = mysqli_connect("my_host", "my_user", "my_pwd", "my_db");
$stmt = mysqli_prepare($conn, $select_invites_query);
mysqli_stmt_bind_param($stmt, "i", $relative_to_id);
mysqli_stmt_execute($stmt);
...

How to Get VALUES from another table into another table in PHP/MySQL

First I should say my English is not perfect.
I am using XAMPP 5.6.0.0
I have two tables, services and services_cat.
-- Table structure for table `services_cat`
CREATE TABLE IF NOT EXISTS `services_cat`
(`ser_cat_id` int(11) NOT NULL,
`service_category` varchar(50) NOT NULL)
-- Table structure for table `services`
CREATE TABLE IF NOT EXISTS `services`
(`service_id` int(11) NOT NULL,
`scid` int(11) NOT NULL,
`service_title` varchar(50) NOT NULL,
`service_statement` varchar(1000) NOT NULL,
`service_photo` varchar(100) NOT NULL)
I want to show following data in one table.
service_id
ser_cat_id
service_category
service_title
service_statement
ser_cat_id and service_id are PRIMARY KEYs.
here ser_cat_id save into scid in the services table. This ser_cat_id save multiple times in the scid colomn. But service_title will be changed. There are different types of service_title, but the scid is same for that different service_titles. That scid comming from ser_cat_id from services_cat table.
I can pass ser_cat_id to scid from service_cat table and I can fetch (display) that scid. But I want to display that DISTINCT service_category name of the ser_cat_id(same as scid in services table).
Can you please help me ..
Following is my code.
<?php
include_once("conn.php");
$sql = mysql_query("SELECT * FROM services_cat JOIN services ON services_cat.ser_cat_id = services.service_id GROUP BY service_category ");
while($row = mysql_fetch_object($sql))
{
echo "<tr>
<td><p class='table-p'>$row->scid</p></td>
<td><p class='table-p'>$row->ser_cat_id</p></td>
<td><p class='table-p'>$row->service_category = $row->scid</p></td>
<td><p class='table-p'>$row->service_title</p></td>
<td><p class='table-p'>$row->service_statement</p></td>
<td><p class='table-p'><a href='service_edit.php?ide=$row->service_id'>Edit</a> |
<a href='service_delete.php?idd=$row->service_id'>Delete</a></p></td>
";
}
?>
Based on what you want, this is what you need:
SELECT s.service_id, c.ser_cat_id, c.service_category, s.service_title, s.service_statement
FROM services_cat AS c
INNER JOIN services AS s
ON c.ser_cat_id = s.scid
Check this running example: http://sqlfiddle.com/#!9/013f7/3/0
Inside your loop, your $row will have this data, as you need.
$row['service_id']
$row['ser_cat_id']
$row['service_category']
$row['service_title']
$row['service_statement']
try to change,
$sql = mysql_query("SELECT * FROM services_cat JOIN services ON services_cat.ser_cat_id = services.service_id GROUP BY service_category ");
to
$sql = mysql_query("SELECT * FROM services_cat JOIN services ON services_cat.ser_cat_id = services.scid GROUP BY service_category ");

How to query the second column of a JOIN table?

I would like to query the name of the friends of person n°4, so the numbers in 'otherPerson_id', but my attempt only query the id or name in 'person_id'. How could I ask to query the informations about the friends of person n°4, and not the information about person n°4 itself?
Here is my attempt :
$q = "SELECT DISTINCT p.idperson, p.name FROM person p INNER JOIN people_friends pf ON p.idperson = pf.person_id AND p.idperson = 4";
$res = $connexion->query($q);
$res->setFetchMode(PDO::FETCH_COLUMN);
while($record = $res->fetch()) {
echo $record[name];
echo "<br/>";
}
the table : (so when I look for the friends of person n°4, I would like it to return 1 and 7 )
EDIT :
CREATE TABLE people_friends
(
person_id integer NOT NULL,
otherperson_id integer NOT NULL,
CONSTRAINT people_friends_pkey PRIMARY KEY (person_id, otherperson_id),
CONSTRAINT people_friends_person_id_fkey FOREIGN KEY (person_id)
REFERENCES person (idperson) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT people_friends_person_id_fkey1 FOREIGN KEY (person_id)
REFERENCES person (idperson) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
CREATE TABLE person
(
idperson integer NOT NULL,
name character varying(50),
map bytea,
CONSTRAINT person_pkey PRIMARY KEY (idperson)
)
Thanks
Try this query.
SELECT DISTINCT
pFriend.idperson, pFriend.name
FROM people_friends pf
INNER JOIN person pFriend on pf.otherperson_id = pFriend.idperson
WHERE
pf.person_id = 4
You just have to add a WHERE clause where you check the pf table.
SELECT DISTINCT p.idperson
,p.name
FROM person p
INNER JOIN people_friends pf
ON p.idperson = pf.person_id
WHERE pf.otherperson_id = 4

Retrieving Data From Associated Toxi Table

I'm retrieving my data for part of my site with a typical MySQL query and echoing out the results from various fields etc etc from my main table whose structure is not important but which has a unique id which is 'job_id'
In order to have multiple catagories associated with that 'job_id' i have employed a toxi solution which associates catgories to each 'job_id'.
TABLE `tags` (
`tag_id` INT NOT NULL AUTO_INCREMENT,
`tag_name` VARCHAR(20) NOT NULL,
PRIMARY KEY (`tag_id`)
)
CREATE TABLE `tag_relational` (
`job_id` INT NOT NULL,
`tag_id` INT NOT NULL
)
What i want to do is, when i echo out the info from the main table (using 'job_id') i also want to echo all the catagories which that job_id is matched against.
The query below only returns the first catagory(tag_name) that the job_id is listed against, when it should be up to six (at the moment):
$query = "SELECT * FROM tags t
JOIN tag_relational r
ON t.tag_id=r.tag_id
WHERE r.job_id = $job_id";
$result=mysql_query($query) or die(mysql_error());
$cats=mysql_fetch_assoc($result);
In my code i'm using this to echo out the matched catagories:
<?php echo $cats['tag_name'];?>
Can someone explain how i can get ALL the catagory names to echo out rather than just the first?
Thanks
Dan
BTW, apologies to mu is too short who kindly answered my question when i had dummy/less complete information above.
If you just want to list the category names, then you could use group_concat sort of like this:
select b.*,
group_concat(c.category_name order by c.category_name separator ' ,') as cats
from business b
join tbl_works_categories w on b.id = w.bus_id
join categories c on w.category_id = c.category_name
where ...
group by b.id
You'd need a proper WHERE clause of course. That will give you the usual stuff from business and the category names as a comma delimited list in cats.
If you need the category IDs as well, then two queries might be better: one to get the business information and a second to collect the categories:
select w.bus_id, c.category_id, c.category_name
from tbl_works_categories w
join categories c
where w.bus_id IN (X)
where X is a comma delimited list of business ID values. Then you'd patch things up on the client side.

How to retrieve certain value(s) through foreign key linking in MySQL?

sorry for the unclear title but I couldn't come up with anything better.
My dilemma is this:
I have one InnoDB table in my database called "meetings" with the following structure:
meeting_id (primary key, auto_increment)
user1_id (foreign key pointing to a user_id in a table called "users")
user2_id (foreign key pointing to a user_id in a table called "users")
time (type DATETIME)
location (type VARCHAR(200))
The table "users" is basic and looks like this:
user_id (primary key, auto_increment)
first_name (type VARCHAR(30))
last_name (type VARCHAR(30))
I have a PHP file with the aim to simply print out a description of the meeting, e.g.:
You saved the following meeting information:
User 1 | User 2 | Time & Date | Meeting location
John Doe | Jane Doe | 2010-10-10 10:10:10 | New York
Now, I simply want to use the meeting ID, call my database (only the "meetings" table) and be able to get the first_name and last_name of the user1 and user2.
Right now, my non-working code looks like this:
$query = "SELECT * FROM meetings WHERE meeting_id = " . $mid;
$data = mysqli_query($dbc, $query);
$row = mysqli_fetch_array($data); // ANY CHANGES HERE?
...
echo '<p>You saved the following meeting information::</p>';
echo '<table>';
echo '<tr><th>User 1</th><th>User 2</th><th>Time & Date</th><th>Meeting location</th></tr>';
echo '<td>' . $row['user1_id']['first_name'] . ' ' . $row['user1_id']['last_name'] . '</td>'; // NON-WORKING
echo '<td>' . $row['user2_id']['first_name'] . ' ' . $row['user2_id']['last_name'] . '</td>'; // NON-WORKING
echo '<td>' . $row['date_time'] . '</td>';
echo '<td>' . $row['location'] . '</td>';
echo '</table>';
...
(How) can I retrieve the first_name/last_name links without making separate calls to the "users" table? When I check phpMyAdmin, the InnoDB foreign key links seem to work fine. Many thanks in advance!
If your query would be
$query = "SELECT time, location, user1.first_name, user1.last_name, user2.first_name, user2.last_name FROM meetings m JOIN users user1 ON m.user1_id = user1.id JOIN users user2 ON m.user2_id = user2.id WHERE meeting_id = " . $mid;
Then the names should be available as
$row['user1.first_name']
and so on.
Foreign keys are not magical devices that always bring in related records (nor should they be).
EDIT:
As a side note - having columns that end with numbers such as user1_id and user2_id normally raise a red flag in the mind of people who understand normalization and database design. Basically it boils down to the question - are you ready to accept that your meetings will support only meetings between two people and two people only? Another question that you should answer is: are you sure you want to distinguish between the 'first' and the 'second' participant of the meeting? (current design will make it harder to answer questions such as - list all the meetings for $user. with current table layout you will have to test both fields separately, which might hurt performance)
Well, you have to utilize the users table to get the data from it. That's endemic. But you can have only one query, which is what I think you're after:
SELECT u1.first_name, u1.last_name, u2.first_name, u2.last_name, m.time, m.location from meetings m inner join users u1 on m.user_id1 = u1.user_id inner join users u2 on m.user_id2 = u2.user_id
From the MySQL docs:
Foreign keys in SQL are used to check
and enforce referential integrity, not
to join tables. If you want to get
results from multiple tables from a
SELECT statement, you do this by
performing a join between them:
SELECT * FROM t1 INNER JOIN t2 ON t1.id = t2.id;
You can't, the user1_id only contains a single number, you have to query the users table to get that information. Luckily, it can be acquired through a join:
SELECT u1.first_name, u1.last_name, u2.first_name, u2.last_name,meetings.time, meetings.location FROM meetings
JOIN users u1 ON meetings.user1_id=u1.user_id
JOIN users u2 ON meetings.user2_id=u2.user_id
WHERE meeting_id =

Categories