how to mysqli inner join with 3 tables - php

In the end I need results to contain all data from all 3 tables. How to join the 3 tables so all is present in the end. Here is what I have so far:
if ($results = $db->query("SELECT * FROM HWData
LEFT JOIN ClassData
ON HWData.class = ClassData.class
LEFT JOIN Judges
ON Judges.groupnum = ClassData.groupnum
ORDER BY HWData.entrynum",
MYSQLI_USE_RESULT)) {
$result_set = mysqli_fetch_all($results, MYSQLI_ASSOC);
echo json_encode($result_set);
The results are not as expected. It's odd that console.log[i]['groupnum']; shows correct groupnum but $("#output").append("<p>"+data[i]['groupnum']); shows only 1's
3 tables as follows:
ClassData
groupnum abbr
0 DQB
0 DQC
1 SUM
1 PP
1 FOS
1 VANF
1 VANS
2 AFCF
2 AFCS
2 ICCS
2 CCS
3 LSUF
3 LTMS
4 HL
4 MC
Judges
judge group
Coleen 4
Daniel 3
Aaron 2
Jeff 1
HWData
entrynum fname class
1 Esteban FOS
2 Dalia PP
3 Sheri
4 Sheri HL
5 Sheri MC
10 Danita ICCS
11 Chris AFCS

Ok, so i figured out you can use multiple joins.
if ($results = $db->query("SELECT * FROM HWData
LEFT JOIN ClassData
ON HWData.Abbr = ClassData.Abbr
LEFT JOIN Judges
ON Judges.groupnum = ClassData.groupnum",
MYSQLI_USE_RESULT)) {
$result_set = mysqli_fetch_all($results, MYSQLI_ASSOC);
echo json_encode($result_set);
$results->close();
}

Related

How should be my mysql table structure?

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.

How to do operation on an output tables in sql or php

I have these tables
courses students_records
--------------------- --------------------------------
NO code CRD name ID NO grade
--------------------- --------------------------------
1 COE200 4 Michael 2255 1 A
2 COE305 3 Michael 2255 2 B+
grades
---------------------
NO. points
---------------------
A 4
B+ 3.5
I wrote this query
SELECT courses.code,student_records.grade,courses.crd,grades.points FROM grades INNER
JOIN student_records ON grades.letter = student_records.grade INNER
JOIN courses ON courses.no = student_records.no WHERE student_records.id=2255;
so the out put of this query will be like this
grades
------------------------------
code grade CRD points
------------------------------
COE200 A 4 4
COE305 B+ 3 3.5
My question is How I can write function in php or query in order to
multiply CRD and points and do sum after multiply like this.
(CRD * points)
(4*4)+(3*3.5)=26.5
You can perform the math in the SQL Select and an aggregation to do the sum.
To get the grade/credits to show, you can use this query:
SELECT
courses.code,
student_records.grade,
courses.crd,grades.points,
courses.crd * grades.points AS grade_credits
FROM grades
INNER JOIN student_records ON grades.letter = student_records.grade
INNER JOIN courses ON courses.no = student_records.no
WHERE student_records.id=2255;
For the sum, you can use most of the same query:
SELECT
student_records.id,
SUM(courses.crd * grades.points) AS sum_grade_credits
FROM grades
INNER JOIN student_records ON grades.letter = student_records.grade
INNER JOIN courses ON courses.no = student_records.no
GROUP BY student_records.id
This would give you results for all students.
This is how you would do it in PHP:
$totalgrade = 0;
while($row = $query->fetch_array()) {
$totalgrade += $row['CRD']*$row['points'];
}
echo $totalgrade;

How to formulate the query to join more than one table

MY tables are as follows
users
id name
1 Michael
2 James
3 John
4 Susie
5 Harvey
products
pid name uploader post_id views exclude groupid
1 learn_java 2 1 21 0 1
2 learn_sql 1 2 8 0 2
3 4 GB DDR3 0 3 5 0 3
4 love jacket 2 4 0 0 5
5 1 TB HDD 3 5 12 1 4
6 kill_ants 3 6 5 0 6
7 2 TB HDD 2 7 2 0 4
8 8 GB DDR3 2 8 18 0 3
9 1 GB DDR2 3 9 7 0 3
product_group
gid name category
1 text 1
2 pdf 1
3 ram 2
4 hdd 2
5 leather 0
6 diy 0
product_category
cid name
1 book
2 electronics
/* forgot about comment field*/
comments
comment_id post_id comment
1 1 ...
2 1 ...
3 2 ...
4 2 ...
5 2 ...
6 3 ...
My Goal:
The product table has 4 types of data.
Products with no uploader( uploader = 0 )
Products that are in review( exclude = 1 )
Products that fall under category = 0(poducts.groupid = product_group.gid AND product_group.category = 0 )
Products that are uploaded by an uploader, not in review and not fall under category = 0( uploader != 0, exclude = 1, poducts.groupid = product_group.gid AND product_group.category != 0)
I only have to consider the 4th type of data. I have to exclude the first three types of data. I have to group these data by their uploader. Say, James have uploaded 3 product, Jones have uploaded 2 product and the rest of the user hasn't uploaded anything.
The query should return this
3 James SUM of views of 3 products
2 Jones SUM of views of 2 products
0 user1 0
0 user2 0
....
....
So if I consider the data of my table, I want to get the data in follwing order
product_num users.id users.name total_views
3 2 James 41(21+18+2)
1 3 Jones 7
1 1 Michael 8
0 5 Harvey 0/NULL
0 4 Susie 0/NULL
I came up with this.
SELECT COUNT(pid) as product_num,
SUM(views) as total_views
users.*
FROM users
INNER JOIN products ON products.uploader = users.id
INNER JOIN product_group ON products.groupid = product_group.category
WHERE exclude = 0
AND product_group.category != 0
Which obviously doesn't work as it doesn't include the users, who hasn't uploaded any product. How to make this work to take these users into account?
EDIT:
SELECT COUNT(pid) as product_num,
SUM(views) as total_views
users.*
FROM users
LEFT JOIN products ON products.uploader = users.id
INNER JOIN product_group ON products.groupid = product_group.category
WHERE exclude = 0
AND product_group.category != 0
GROUP BY users.id
ORDER BY product_num
It also doesn't take users with 0 upload.
Second EDIT:
I have added a comment table(I forgot about it earlier). Is there any way to show the total_number of comments for a user.
Here, James has uploaded product 1, 4, 8. For these post_id is also 1, 4, 8(In real these won't be same). From comments table, these posts have following number of comments 2, 0, 0. So, total number of comment 2.
So, final result should be
product_num users.id users.name total_views total_comments
3 2 James 41(21+18+2) 2
1 3 Jones 7 0/NULL
1 1 Michael 8 3
0 5 Harvey 0/NULL 0/NULL
0 4 Susie 0/NULL 0/NULL
Try this query:
SELECT COUNT(pid) as product_num,
SUM(views) as total_views,
u.*
FROM users u
LEFT JOIN products p
ON p.uploader = u.id
LEFT JOIN product_group pg
ON p.groupid = pg.gid
WHERE p.exclude = 0
AND p.uploader <> 0
AND pg.category != 0
OR p.pid is null
GROUP BY u.id
demo: http://sqlfiddle.com/#!2/c94bef/12
To count a number of comments, please add a dependent subquery to the SELECT clause:
SELECT count(*)
FROM comments c
WHERE c.post_id = p.post_id
in this way:
SELECT COUNT(pid) as product_num,
SUM(views) as total_views,
( SELECT count(*)
FROM comments c
WHERE c.post_id = p.post_id
) As total_comments,
u.*
FROM users u
LEFT JOIN products p
ON p.uploader = u.id
LEFT JOIN product_group pg
ON p.groupid = pg.gid
WHERE p.exclude = 0
AND p.uploader <> 0
AND pg.category != 0
OR p.pid is null
GROUP BY u.id
demo: http://sqlfiddle.com/#!2/5c44f/1

PHP MySQL Highscore table

Im joining 3 tables to present a table with users highest score
My tables
game_log:
---ID---user_ID---score---time---
| 1 52 567 10 |
| 2 53 641 13 |
| 3 52 465 8 |
| 4 53 451 14 |
---------------------------------
users:
---ID---name---countyid---
| 52 Dave 1 |
| 53 John 2 |
------------------------
county:
---countyid---countyname---
| 1 Orange wichit |
| 2 Orange clemts |
--------------------------
SQL:
SELECT * FROM game_log
INNER JOIN users ON game_log.user_ID=users.ID
INNER JOIN county ON users.countyid=county.countyid
ORDER BY game_log.score DESC , game_log.time LIMIT 20";
Above code gives me this result:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
3 John Orange clemts 465 8
4 Dave Orange wichit 451 14
My problem is that I want the highscore table to display the top 20 users with the highest score, not the 20 highest scores.
Like this:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
Need som help with this, not familiar with joining tables ;-)
This approach will show the top 20 users and each user's highest score, and if they have multiple instances of the same score, it'll show the information for the earliest one (lowest time value for that user and score).
SELECT *
FROM game_log gl
INNER JOIN users u
ON gl.user_ID = u.ID
INNER JOIN county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.score > gl.score)
and not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.time < gl.time
and gl2.score = gl.score)
ORDER BY gl.score DESC, gl.time LIMIT 20;
Without doing this, if the same user in the top 20 had the same score 2+ times, they would be listed 2+ times, and you would not get back 20 people by using LIMIT 20 because the same person would be taking up N rows out of that 20.
SQL Fiddle here showing data with a tie: http://sqlfiddle.com/#!2/0ac931/5/0
GROUP BY should do the job.
SELECT users.ID, users.name, county.countyname, MAX(game_log.score) AS score, game_log.time
FROM game_log
INNER JOIN users ON game_log.user_ID = users.ID
INNER JOIN county ON users.countyid = county.countyid
GROUP BY game_log.user_ID
ORDER BY game_log.score DESC, game_log.time
LIMIT 20;
Try it out with SQL Fiddle.
I would do this with the not exists approach to get the highest score for each user. The rest of the query is the same:
SELECT *
FROM game_log gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id and gl2.score > gl.score
)
ORDER BY gl.score DESC, gl.time
LIMIT 20;
The where clause is saying "keep this row if no other row for the same user has a higher score".
Another way to do this is with the aggregation approach:
SELECT *
FROM (select user_id, max(score) as maxscore
from game_log gl
group by user_id
) gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
ORDER BY gl.maxscore DESC
LIMIT 20;
But this method loses the information about time. It is possible to include that, but it makes the query more complicated.

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