How should be my mysql table structure? - php

This is what I want:
Users will send one or two values in my website and I will store them in two variables $genres1 and $genres2.
Like: If user sends, Action, then my code will show all movies with Action genres. If user sends Action+Crime, then my table will fetch all movies with Action+Crime.
Got it?
My current table structure is one to many relationship, like this
tmdb_id movie_title
-----------------------------
1 Iron man
2 Logan
3 Batman
4 The hangover
tmdb_id genres
-----------------------------
1 Action
1 Crime
2 Drama
2 Action
3 Crime
3 Action
4 Comedy
4 Drama
But the problem here is, I can't achieve what I explained above with this.
2nd option: I make a single table like this:
movie_tile genres1 genres2 genres3 genres4
----------------------------------------------------
Logan Action Crime Drama Null
Iron man Action Crime Null Null
And I can do what, I want with this single line:
SELECT * FROM movies WHERE (genres1='$genres1' or genres2='$genres1' orgenres1='$genres3' or genres3='$genres1')
Any other option?

use a table width genres
and use an other table connecting the movie to any genre
-----
movieid title
-----
1 Logan
2 Smurf
-----
-----
genreid genre
-----
1 animated
2 blue people
-----
-----
movieid genreid
-----
1 1
2 1
2 2
-----
that way you won't be limited to 4 genres per movie
now I read your question better.
That's what you do, but you put left out the genre-table.
The 2nd option is bad, as you limit yourself to only 4 categories
Is this connected to PHP? I think is easiest to solve this further by a join query, sorted by movie and a loop in PHP
you want all movies where (by user request) the genres are both Crime And Action?
SELECT mg.movieid, count(1), m.title
FROM movies_genres mg
JOIN movies m ON m.movieid mg.movieid
WHERE mg.genreid = 1 OR mg.genreid =3
group by mg.movieid, m.title
HAVING COUNT(1) = 2
edit: see other genres as well
SELECT movies.movieid,movies.title, genres.genre
FROM movies
JOIN movie_genre mg ON mg.movieid = movies.movieid
JOIN genres on genres.genreid = mg.genreid
WHERE movie.movieid IN (
SELECT mg.movieid
FROM movies_genres mg
WHERE mg.genreid = 1 OR mg.genreid =3
GROUP BY mg.movieid
HAVING COUNT(1) = 2
)
forgot to mention: count = 2, means you gave 2 genreid's to find. This could also be 1, 3 or 25

select distinct a.tmdb_id, a.movie_tittle
from movie_tittle a inner join genre_tittle b
on a.tmdb_id = b.tmdb_id
where b.genres in ('Action', 'Crime')
Based on your comment, try this :
SELECT
a.tmdb_id, a.movie_tittle
FROM
movie_tittle a inner join genre_tittle b
ON
a.tmdb_id = b.tmdb_id
WHERE
b.genres in ('Action', 'Crime')
GROUP BY
a.tmdb_id, a.movie_tittle
HAVING
count(a.tmdb_id) = 2
tmdb_id and genres in table genre_tittle should not duplicated. Make it as primary key.

But the problem here is, I can't achieve what I explained above with [the first two tables]
Yes, you can. Assuming the two tables are called movies and movie_genres, you can select the movies which have both tags using:
SELECT movie_title FROM movies
JOIN movie_genres genre1 USING (tmdb_id)
JOIN movie_genres genre2 USING (tmdb_id)
WHERE genre1.genres = 'Action'
AND genre2.genres = 'Crime'
See it for yourself here.

try something like this :
tableA
Movie_ID Movie_title
1 Iron man
2 Logan
3 Batman
4 The hangover
tableB
Genre_ID Genre_title
1 Action
2 Crime
3 Drama
4 Comedy
tableC
ID Movie_ID Genre_ID
1 1 1
2 1 2
3 2 2
4 2 3
query :
Select A.Movie_title,B.Genre_title
from tableC C
inner join tableA A on A.Movie_ID = C.Movie_ID
inner join tableB B on B.Genre_ID = C.Genre_ID
where
C.Genre_ID in (IFNULL(val1,0),IFNULL(val2,0))

you should make a relational table to solve you issues like so
movie table
movie_id movie_name genre_id
1 alien 2
2 logan 1
3 ps i love you 4
4 click 3
then you will need a genre table
genre table
genre_id genre_type
1 action
2 sci fi
3 comedy
4 romance
then your select would link the to tables
function get_movies_by_genre($genre_id) {
global $MySQLiConnect;
$query = '
SELECT *
FROM movies m
INNER JOIN genre g ON (g.genre_id = m.genre_id)
WHERE g.genre_id = ?
';
$stmt = $DBConnect->stmt_init();
if ($stmt->prepare($query)) {
$stmt->bind_param("i", $genre_id);
$stmt->execute();
$result = $stmt->get_result();
$rows = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
}
return $rows;
}
or
function get_movies_by_genre($genre_id) {
global $MySQLiConnect;
$query = '
SELECT *
FROM movies m
INNER JOIN genre g ON (g.genre_id = m.genre_id)
WHERE g.genre_name = ?
';
$stmt = $DBConnect->stmt_init();
if ($stmt->prepare($query)) {
$stmt->bind_param("i", $genre_id);
$stmt->execute();
$result = $stmt->get_result();
$rows = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
}
return $rows;
}
This is the base function to get you all information from the movie table depending on which genre id you send to it.
as for multiple ids you can then run the function through a foreach loop for as many genre_ids as you need and then display them as you need.
I hope this helps.

Related

SQL Query with two different filters

I have a page which shows all cars from DB.
I have two filters , both are multiple select filter.
For example
filter 1 - Color
Red , green , blue <-- All these are checkbox ,can be selected multiple
filter 2 - brand
BMW, Honda , Hyundai <-- All these are checkbox ,can be selected multiple
I have done below query
Select * from cars
JOIN term_car_relationships
ON cars.id = term_cars_relationships.car_id
WHERE term_cars_relationships.term_id in (6,2,3)
GROUP BY cars.id
In above query term_id
6 = blue (Color)
2 = green (Color)
3 = BNW (brand)
But with above query I will get all cars which has blue color or green color or BMW brand
But how to change in such a way that I get BMW which is blue or green color.
I have 3 tables which handles these categories.
taxonomy table
taxonomy_id | taxonomy_title
1 | Color
2 | Brand
term_list
term_id | term_name | taxonomy_id
1 | Blue | 1
2 | Red | 1
3 | BMW | 2
4 | Honda | 2
term_cars_relationships Table
term_id | car_id
1 | 1
1 | 2
2 | 3
You should join the term_cars_relationships table twice:
SELECT * FROM cars
JOIN term_car_relationships c ON cars.id = c.car_id
JOIN term_car_relationships b ON cars.id = b.car_id
WHERE c.type_of_category = 'color'
AND b.type_of_category = 'brand'
AND c.term_id in (6,2)
AND b.term_id in (3)
GROUP BY cars.id
Note that I used b.term_id in (3) instead of b.term_id = 3 since I assumed you might want to select multiple brands.
You can construct your query with separate joins for each term category, and separate filter conditions for each as well.
SELECT cars.*, colorTerms.*, brandTerms.*
FROM cars
INNER JOIN term_car_relationships AS carColorTerms
ON cars.id = carColorTerms.car_id
INNER JOIN term_list AS colorTerms
ON carColorTerms.term_id = colorTerms.term_id AND colorTerms.taxonomy_id = 1
INNER JOIN term_car_relationships AS carBrandTerms
ON cars.id = carBrandTerms.car_id
INNER JOIN term_list AS brandTerms
ON carBrandTerms.term_id = brandTerms.term_id AND brandTerms.taxonomy_id = 2
WHERE colorTerms.term_id IN (6, 2)
AND brandTerms.term_id IN (3)
Of course, to construct this query, you will need to know the terms' types before the query.
Also, using GROUP BY cars.id without aggregation is probably a sign of a problem, or just not the right way to get what you want. If you only want the information from the cars table, you should just SELECT DISTINCT cars.*. Using GROUP BY in this manner will end up with results with the data from just one color-brand match for each car.
With the complexity the edit to the original question added, another possibility presents itself....
SELECT cars.* -- You should really just select the fields you want, and may have to in some configurations (see GROUP BY)
FROM cars AS c
INNER JOIN term_car_relationships AS tcr ON c.car_id = tcr.car_id
INNER JOIN term_list AS tl ON tcr.term_id = tl.term_id
WHERE tcr.term_id IN (6, 2, 3)
GROUP BY cars.car_id -- In some configurations of MySQL, and most other RDBMSes, you must specify every SELECTed non-aggregated field here
HAVING COUNT(DISTINCT tl.taxonomy_id)
= ( SELECT COUNT(DISTINCT taxonomy_id)
FROM term_list
WHERE term_id IN (6, 2, 3)
)
Note: This final solution does not actually require you to know term taxonomies ahead of time anymore, and does not grow as more taxonomies need supported; so while it is a little less obvious with what it is doing, is probably definitely worth considering.

SQL display and fetch GROUP BY categories

I have a list of authors and I would like to display it by book genre, like this:
Horror
Stephen King
Author 2
Author 3
...
Comedy
Author 4
Author 5
...
In my database I have an author table:
t_author
aut_name | aut_genre
Stephen King | 1
...
...
Author 4 | 2
And a genre table :
t_genre
genre_id | genre_name
1 | Horror
2 | Comedy
...
...
I've tried to use a GROUP BY but I can't find how to display the names of the category and organize my list:
SELECT t_author.aut_name, t_author.aut_genre
FROM t_author
LEFT JOIN t_genre ON t_author.aut_genre = t_genre.genre_id
GROUP BY t_genre.aut_genre
So, using MySQL and PDO, is there a way to select and fetch all the authors and display it by categories?
No need to say that I'm not looking for a code but just a clue because I don't even see how to describe it. I mean, is GROUP BY the right statement to use?
EDIT 1 :
Apparently, I was looking for something like this :
SELECT t_author.aut_name, t_author.aut_slug, t_genre.genre_name
FROM t_author
LEFT JOIN t_genre ON t_author.genre_id = t_genre.genre_id
But it displays:
Horror
Stephen King
Horror
Author 2
Comedy
Author 4
Horror
Author 3
I perfectly understand why but I can't find how to display it like below:
Horror
Stephen King
Author 2
Author 3
...
Comedy
Author 4
Author 5
...
Answer
HtmHell provided me a good way to do this with a 2D array. For my 3D array, I edited his code like this:
foreach($result as $author=> $slug) {
$authorsByGenre[$slug['genre_name']][] = array($slug['aut_name'] => $slug['aut_slug']);
}
return $authorsByGenre;
How about something like this:
$authorsByGenre = [];
$results = $db->query("
SELECT t_author.aut_name, t_genre.genre_name
FROM t_genre
JOIN t_author
ON t_author.aut_genre = t_genre.genre_id
")->fetchAll();
foreach ($results as $data) {
$authorsByGenre[$data['genre_name']][] = $data['aut_name'];
}
print_r($authorsByGenre);
SELECT GROUP_CONCAT(t_author.aut_name) as authors, t_genre.genre_name as genre FROM t_author
LEFT JOIN t_genre ON t_author.aut_genre = t_genre.genre_id
GROUP BY t_author.aut_genre
This will output authors as author1,author2,author3 in column 1 and genre column with genre_name so you can split or explode authors or whatever
Like it was suggested in the comments, PDO::FETCH_GROUP is the answer to your question. Dunno why you ignored it
$sql = "SELECT genre_name, aut_name, aut_slug
FROM t_author a LEFT JOIN t_genre g ON a.genre_id = g.genre_id";
$authorsByGenre = $db->query($sql)->fetchAll(PDO::FETCH_GROUP);
print_r($authorsByGenre);

how can select data from two tables in php

I have two tables news and topics
news topics
id title text id title text
--- ----- ---- --- ----- ----
1 abc aa 1 hgd hh
2 def bb 2 ddf ff
3 gfs cc 3 gty gg
4 sdfv dd 4 bbc tt
i want to select title the two tables
i tried this code but it sin't work
<?
$select_newtopics = $mysqli->query("select * from news,arts order by id desc");
$num_newtopics = $select_newtopics->num_rows;
while ($rows_newtopics = $select_newtopics->fetch_array(MYSQL_ASSOC)){
$id_newtopics = $rows_newtopics ['id'];
$title_newtopics = $rows_newtopics ['title'];
echo $title_newtopics."<br>";
}
?>
Assuming you want a result set that contains all the (unique) titles from both tables, regardless of whether or not the tables are related in any way...
SELECT title FROM news
UNION
SELECT title FROM topics
See MySQL's UNION documentation for more details, options, and specifics.
To display the results, it's mostly the same as your previous code:
$select_newtopics = $mysqli->query("SELECT title FROM news
UNION
SELECT title FROM topics");
$num_newtopics = $select_newtopics->num_rows;
while ($row = $select_newtopics->fetch_array(MYSQL_ASSOC)) {
echo $row['title'] . '<br>';
}
Since I don't see a key tying the 2 tables together, you can use UNION reference google for additional info, example below:
SELECT title FROM news
UNION
SELECT title FROM topics;
Your table have to share an id for example added news_id to topics table to link the two table
news topics
id title text id title text news_id
--- ----- ---- --- ----- ---- -------
1 abc aa 1 hgd hh 1
2 def bb 2 ddf ff 2
3 gfs cc 3 gty gg 3
4 sdfv dd 4 bbc tt 4
Please update your database structure and you can use JOIN
<?
$select_newtopics = $mysqli->query("SELECT news.id AS newsId,
news.title AS newsTitle,
news.text AS newsText,
topics.id AS topicId,
topics.title AS topicTitle,
topics.text AS topictext
FROM topics
LEFT JOIN news ON topics.news_id = news.id
ORDER BY topics.id
DESC");
$num_newtopics = $select_newtopics->num_rows;
while ($rows_newtopics = $select_newtopics->fetch_array(MYSQL_ASSOC)){
$id_newtopics = $rows_newtopics ['topicId'];
$title_newtopics = $rows_newtopics ['topicTitle'];
echo $title_newtopics."<br>";
}
?>

Result display problem in php and mysql

A B C
P_id | name | add P_id | t_id t_id | paper
----------------- -------------- ------------------
1 sam ca 1 1 1 asdxa
2 john de 2 1 2 dgfv
3 sam jk 3 2 3 decgf
4 sam ca 4 3 4 ergvtr
Now I can easily make a search for Name 'sam' in Table A group by name, add. and It shows me result like,
sam ca
sam jk
I am using php and mysql. I want to do some additional task in this:-
--> Totalcount for the rows..Like
sam ca 2
sam jk 1
(I am not taking P_id here... just focus on name and add.)
--> Make a link on 2 and 1 (above). so if I click on it. It should displays the related papers on another html page from table C.
example: if I click on 2... then it should display asdxa and decgf.
--> Totalcount for the rows..Like
SELECT `name`, `add`, count(`add`) FROM `A` WHERE (`name` = 'sam') GROUP BY `add`;
As for the linking, you just link to the page with that ID, and have your php script get the data from the given id in the C table.
To select all matching occurences while searching for a name:
SELECT `A`.`name` AS name, `A`.`P_id` AS aid, `C`.`t_id` AS cid, `C`.`paper` AS paper
FROM `A` , `C`
WHERE (
`A`.`name` = 'sam'
)
AND (
`A`.`P_id` = `C`.`t_id`
)
result:
name aid cid paper
sam 1 1 qwertyui
sam 3 3 zxcvbn
sam 4 4 uytrewq
sam 5 5 hfdsa
Which matches the test tables I did in my local environment
Try to use for the first task
$pdo = new PDO(....);
$result = $pdo->query('SELECT name, add, count(*) from table where name='sam' group by add;')->fetchAll();
if ($result) {
foreach($result as $row) {
......
}
}
//second
$sql = 'select paper from tableA a inner join TableB b on a.P_id=b.P_id inner join Tablec c inner join b.t_id = c.t_id where a.p_id=2';
//repeat previous statements

Help with limiting a joined mysql database query

I have written a query which returns all records with some many-to-many joins correctly for the entire set or an individual article using WHERE a.id = ?
SELECT a.id, date_added, title, content, category_id, person_id, organization_id, c.name AS category_name, firstname, lastname, o.name AS organization_name
FROM articles AS a
LEFT OUTER JOIN articles_categories AS ac ON a.id=ac.article_id
LEFT OUTER JOIN categories AS c ON c.id=ac.category_id
LEFT OUTER JOIN articles_people AS ap ON a.id=ap.article_id
LEFT OUTER JOIN people AS p ON p.id=ap.person_id
LEFT OUTER JOIN articles_organizations AS ao ON a.id=ao.article_id
LEFT OUTER JOIN organizations AS o ON o.id=ao.organization_id
ORDER BY date_added
BUT!
I've hit a brick wall trying to work out how to limit the articles to a specific number of IDs, for working with pagination.
I'm ideally trying to use as simple and clear SQL statements as possible because I'm using the codeigniter framework with their active record class.
http://codeigniter.com/user_guide/database/active_record.html
Would really appreciate some help as I don't want to revert to using multiple queries for this as I've tried to reduce it down to a single query for database efficiency.
Have search around and tried some alternatives but nothing seems to work. Many thanks!
For example the results I return are like this
---------------------------------------------------------------------
id title category_id person_id organization_id
---------------------------------------------------------------------
1 test 1 1 1
1 test 2 1 1
1 test 1 2 1
1 test 1 1 2
1 test 5 1 1
1 test 8 1 1
1 test 1 4 1
1 test 1 4 2
1 test 1 1 1
2 test 2 2 1 1
2 test 2 1 2 1
2 test 2 1 1 2
2 test 2 5 1 1
2 test 2 8 1 1
2 test 2 1 4 1
2 test 2 1 4 2
I need the results like this so that I can create sub-arrays in the php like this:
$articles = $query->result_array();
$output = array();
foreach ($articles as $article) {
// set up article details
$article_id = $article['id'];
// add article details
$output[$article_id]['article_id'] = $article_id;
$output[$article_id]['date_added'] = $article['date_added'];
$output[$article_id]['title'] = $article['title'];
$output[$article_id]['content'] = $article['content'];
// set up people details and add people array with details if exists
if (isset($article['person_id'])) {
$person_id = $article['person_id'];
$output[$article_id]['people'][$person_id]['person_id'] = $person_id;
$output[$article_id]['people'][$person_id]['lastname'] = $article['lastname'];
$output[$article_id]['people'][$person_id]['firstname'] = $article['firstname'];
}
// set up organizations details and add organizations array with details if exists
if (isset($article['organization_id'])) {
$organization_id = $article['organization_id'];
$output[$article_id]['organizations'][$organization_id]['organization_id'] = $organization_id;
$output[$article_id]['organizations'][$organization_id]['organization_name'] = $article['organization_name'];
}
// set up categories details and add categories array with details if exists
if (isset($article['category_id'])) {
$category_id = $article['category_id'];
$output[$article_id]['categories'][$category_id]['category_id'] = $category_id;
$output[$article_id]['categories'][$category_id]['category_name'] = $article['category_name'];
}
}
But if I just use LIMIT (with offset etc) 1
the results I get are
---------------------------------------------------------------------
id title category_id person_id organization_id
---------------------------------------------------------------------
1 test 1 1 1
instead of
---------------------------------------------------------------------
id title category_id person_id organization_id
---------------------------------------------------------------------
1 test 1 1 1
1 test 2 1 1
1 test 1 2 1
1 test 1 1 2
1 test 5 1 1
1 test 8 1 1
1 test 1 4 1
1 test 1 4 2
1 test 1 1 1
which is my desired result.
OK, so finally I worked out how it is possible.
Thought i'd include it here in case anyone else has the same problem.
Changing this line
FROM articles AS a
to this
FROM (SELECT * FROM articles LIMIT 5,3) AS a
does what I wanted.
So, why don't you use OFFSET 0,10 and LIMIT *number_of_results* in the SQL Query? (if I understood the question)
Specific number of IDs... WHERE ID IN (2,4,6,8)... ?
Are you using codeigniter's pagination?
http://codeigniter.com/user_guide/libraries/pagination.html
You can easily limit the number of records that are being returned using the MySQL LIMIT clause. This can be achieved like the following with your sample query.
SELECT a.id, date_added, title, content, category_id, person_id, organization_id, c.name AS category_name, firstname, lastname, o.name AS organization_name
FROM articles AS a
LEFT OUTER JOIN articles_categories AS ac ON a.id=ac.article_id LEFT OUTER JOIN categories AS c ON c.id=ac.category_id
LEFT OUTER JOIN articles_people AS ap ON a.id=ap.article_id LEFT OUTER JOIN people AS p ON p.id=ap.person_id
LEFT OUTER JOIN articles_organizations AS ao ON a.id=ao.article_id LEFT OUTER JOIN organizations AS o ON o.id=ao.organization_id
ORDER BY date_added
LIMIT 10
Where 10 is the number of records you wish to display. The MySQL LIMIT clause allows you to specify a limit of the number of records and an initial offset. Like so:
LIMIT <offset>,<limit>
In your case <offset> would be the current page * the number of records on a page. <limit> would be the number of records you would like to display per page.

Categories