Joining Three Tables - php

I need help with joining three tables, this image shows the fields and table names.
Extract from database:
voting table
id name closed date votes_up votes_down
4 29192 0 1467441761 32 14
21 14427 0 1467450299 1 0
20 14464 0 1467449751 0 0
14 27345 0 1467447075 0 0
9 27329 0 1467443096 1 0
games table
id id_gamepressure id_gamepressure2 title
256 8228 10759 Yu-Gi-Oh! World Championship 2007
512 2520 9294 Boulder Dash Rocks!
768 10155 12664 SpongeBob vs. The Big One. Beach Party Cook-Off
1024 9587 8484 Nacho Libre
images table
id id_game id_gamepressure filename
30695 10001 173814 10001_deca-sports_11792.jpg
30690 10001 173819 10001_deca-sports_1524.jpg
30692 10001 173817 10001_deca-spoets_3551.jpg
30694 10001 173815 10001_deca-sportss_572.jpg
30693 10001 173816 10001_deca_sports_8866.jpg
30691 10001 113818 10001_deca-sports_9417.jpg
Basically I want to be able to find the most popular games via from the "voting table" using the field "votes_up" then linking matching it with the "games table" and finally with the "images table"
I want to display it in list form, sorted by the highest voted game.
like this:
game title (rating) (image)

Something like this, I dont know the actual table names from your image. Or what fields you want.
SELECT
g.id,
i.id //or whatever fields you want.
FROM
games AS g
INNER JOIN
images AS i
ON
g.id = i.id_game
LEFT JOIN
votes AS v
ON
g.id = v.name
ORDER BY
v.votes_up
DESC
In this case as votes is already a sum you dont have to count them like I was thinking, ( votes would be 1 row per vote, that is what I was thinking )
Id use a left join, but I supose as you want the most up voted game it wouldn't really matter.
make sure the ids are indexs as well as the vote counts or sorting will kill the performance.

Related

Joining tables for a comment list and determine if the user already upvoted/downvoted the comment

I've been building a comment system for a project of my own and the way I currently determine if the currently logged user has voted (up or down) on the comment is not very.. smart. Right now I query the database everytime a comment is going to be displayed, which is not ideal for even 100+ comments per page. That means 100+ extra queries.
I tried using JOIN and LEFT JOIN and LEFT OUTER JOIN and I can't figure out how to do this by myself. Nothing that I tried gives me the result I need.
This is what I came up with but I can't figure out how to only merge the values where the user has voted.
$q_get_comments = $conn->prepare('
SELECT comments.id,
comments.parent,
comments.subpage,
comments.author_id,
comments.author,
comments.message_cut,
comments.regdate,
comments.points,
votes_comments.user_id,
votes_comments.user_name,
votes_comments.comm_id,
votes_comments.direction
FROM comments LEFT JOIN votes_comments
ON comments.id = votes_comments.comm_id
WHERE comments.subpage= :subpage
ORDER BY points DESC, regdate DESC');
$q_get_comments->execute(array(':subpage' => $sub_id));
and my tables are set up like this:
Comments
id parent subpage author_id author message message_cut ip regdate points up down
------------------------------------------------------------------------------------------
1 0 68 6 name msg <p>msg</p> x date 5 4 0
2 0 68 6 name msg <p>msg</p> x date 3 2 0
3 2 68 6 name msg <p>msg</p> x date 2 3 2
Votes
id user_id user_name comm_id direction
------------------------------------------
1 6 Chris 2 0
2 6 Chris 3 2
3 6 Chris 1 1
votes.comm_id matches comments.id and that's the way I tried to join the tables.
But I only want to join the results where user_id = 6 and still show all the comments for the subpage, and only add.. say.. the user_id and direction to the according result in comments
The result I need with direction's value at the end to determine the vote status.
id parent subpage author_id author message message_cut ip regdate points up down direction
------------------------------------------------------------------------------------------
1 0 68 6 name msg <p>msg</p> x date 5 4 0 0
2 0 68 6 name msg <p>msg</p> x date 3 2 0 NULL
3 2 68 6 name msg <p>msg</p> x date 2 3 2 2
If direction is NULL, or blank field maybe, the user did not vote on this comment, if it has a value he did, or something along these lines.
( in the code direction 0 means a revoked/neutral vote, 1 means upvote and 2 means downvote, so if direction exists I can know that the user voted on this comment in some way, and I can show the right html for his vote )
Thank you in advance for any help or tips you can give!
Simply make 2 queries (sudo code, just showing sql, not the actual db calls) to select comments and votes, then merge the results in php:
$comments = $db->prepare('SELECT * FROM comments WHERE subpage = :subpage');
$comments->execute(array('subpage' => $subpage_id));
$commentsById=array();
foreach($comments as $comment)
$commentsById[$comment['id']]=$comment
$votes = $db->prepare('SELECT * FROM votes WHERE user_id = :user_id AND comm_id IN (' . implode(",", array_keys($commentsById)) . ')');
$votes->execute(array('user_id' => $user_id)); // the user_id of the user
foreach($votes as $vote)
$commentsById[$vote['comm_id']]['direction'] = $vote['direction'];
var_dump($commentsById);

Find out where a row ranks in a list of rows with different values

I'm trying to create a ranking system for musicians, in my mysql table I have them listed like this:
id genre artist rating votes
1 rock pink floyd 138 25
2 rock black sabbath 149 28
3 pop katy perry 98 12
4 rock the who 72 14
Users may rate an artist on a scale of 1-10, on a vote I just increase the number (1-10) as rating to the table and increase the vote by 1, so to determine the rank I would divide the rating by votes and that would be the ranking.
I understand how to create an overview which displays all of the artists with the rank,I am however trying to find an efficient way to determine the ranking of a specific artist.
Long story short: If I was to be on the black sabbath sub page, how would I determine its ranking in the rock genre ?
I hope the explanation is understandable.
SELECT COUNT(mi.genre) + 1 rank
FROM musician m
LEFT JOIN (
SELECT genre, ROUND(COALESCE(rating/votes,0),10) rating_per_vote
FROM musician
) mi
ON mi.genre = m.genre
AND mi.rating_per_vote > ROUND(COALESCE(m.rating/m.votes,0),10)
WHERE m.artist = 'black sabbath';
DEMO
UPDATE
You can actually drop the ids from the inner table, I did some playing around.
You do however need the ROUND() to make sure that the comparisons are correct.
Another solution, with less subqueries:
SELECT
m.artist,
r.rank,
r.average
FROM
musician m
JOIN
(
SELECT
id,
#rank := #rank + 1 rank,
IF(votes = 0, 0, ROUND(rating/votes, 10)) average
FROM
musician, (SELECT #rank := 0) uv
WHERE genre = 'rock'
ORDER BY average DESC
) r
ON
r.id = m.id
WHERE m.artist = 'black sabbath'

Mysql select max maximum sum values

I have a database which contains some picture data and a linking table. The tables are build-up like this:
---pictures---
picid Lat Lon
1 5 6
2 7 31
3 31 43
4 -3 35
---user2pictures---
picid userid vote
1 1 1
1 2 1
3 1 -1
3 2 1
4 2 -1
The table pictures contains a picture id and some data about the image, the table user2votes contains vote data from the images. Each user is able to vote on images, but they can only vote 1 time, so the vote will be either 1 (like) or -1 (dislike).
I want to select everything from the pictures table from pictures which have the highest number of votes. Pseudoquery which might explain better what I want:
SELECT * FROM pictures WHERE (SELECT MAX(SUM(vote)) FROM user2pictures LIMIT 12
In this example picture 1 would return at the top, picture 3 would follow and picture 4 as the last one. I really don't know how to solve this, some help in the right direction would be very much appreciated!
Thanks!
The answer is to JOIN the tables, SUM the votes, and ORDER high-to-low by the sum of the votes
SELECT pictures.*, SUM(user2pictures.vote) AS VoteTotal
FROM pictures
JOIN user2pictures ON pictures.picid = user2pictures.picid
GROUP BY pictures.picid
ORDER BY VoteTotal DESC
LIMIT 12
try this
select p.`picid`, `Lat`, `Lon` from pictures p
inner join user2pictures u2p
on p.picid = u2p.picid
group by u2p.picid
order by sum(vote) desc
limit 12
DEMO
I'll assume that you also want to show the pictures with no votes. So, you can try this:
select
p.picId, sum(v.vote) as votes
from
pictures as p
left join user2pictures as v on p.picId = v.picId
group by
p.picId
order by
sum(v.vote) desc
limit 12;
The left join is what lets you show the pictures with no votes (the column votes will have the value 0)
Hope this helps

Counting number of times a value occurs and sending count to another column?

I have a table that I have fed data into through a PHP script, and am managing it using phpMyAdmin. My table has 4 columns. The first is an auto increment, second and third are values being fed in, and the final is meant to keep track of how many times the value from column 3 has appeared.
This is how my table currently appears
RowNumber UserID SongID Plays
1 540 2191 0
2 540 2671 0
3 550 3891 0
4 550 2191 0
5 550 2671 0
6 560 9391 0
7 560 2191 0
I want to search through the whole table and change the value in the Plays column to show how many times the value appears in the table.
Ideally, this is how I want my table to output:
RowNumber UserID SongID Plays
1 540 2191 3
2 540 2671 2
3 550 3891 1
4 550 2191 3
5 550 2671 2
6 560 9391 1
7 560 2191 3
Is there a way to search through the table and update these values?
The amount of data being inputted into the table is quite large, so an efficient solution would be greatly appreciated.
Consider using a view instead of a table, unless you need the value cached for performance reasons. You can compute the count of each value in a subquery and join the results back to the table like so:
SELECT Table.RowNumber, Table.UserID, Table.SongID, x.Plays
FROM Table
INNER JOIN (
SELECT SongID, COUNT(*) AS Plays
FROM Table
GROUP BY SongID
) x
ON Table.SongID = x.SongID;
And create a view from it using CREATE VIEW TableWithPlays AS SELECT .... Having an index on SongID will allow the subquery to complete rather quickly, and you will never have to worry about the Plays column being up to date.
If you do in fact want to cache the values, use an UPDATE query based on the above query:
UPDATE Table a
INNER JOIN (
SELECT SongID, COUNT(*) AS Plays
FROM Table
GROUP BY SongID
) b
ON a.SongID = b.SongID
SET Plays = b.Plays;
As with the view solution, don't forget the index on SongID.
I think you can use simple PHP query that is run periodically (Note: not an actual code):
$sql = "SELECT UserID, SongID, COUNT(RowNumber) AS CNT FROM SomeTable GROUP BY 1, 2 ORDER BY 3 ASC";
foreach($result as $row){
$sql = "UPDATE SomeTable SET Plays = ".$row['CNT']." WHERE UserID = '" . $row['UserID'] . "' AND SongID = '" . $row['SongID'] . "'";
}

Advanced mysql queries, fetching from several tables and rows at once. Joins?

I'm currently working on a project where I have information stored in several tables that all connect to each other. I believe that the table and column format is logical and the best choice. The problem though, is that I don't have enough knowledge to build queries advanced enough to fetch all the information I need.
The main table is ab_ads, where advertisements are stored. These ads can be assigned several formats (ie. 250x360, 980x120 etc), and you can also select the region where they should be showing (ie. Skåne, Stockholm, Kalmar, Dalarna, Jämtland etc).
This is how I store my data. I'm not showing all tables but I hope this is sufficient.
Advertisements column (ab_ads): (There are more columns but they are not relevant)
ID orgnum company_name title content link
1 556664-7524 Company Inc Lorem ipsum Lorem ipsum URL
Advertisement states (ab_ads_states):
ID adID stateID
1 1 2 // Skåne
2 1 5 // Kalmar
3 1 8 // Stockholm
4 1 10 // Värmland
5 2 2 // Skåne
6 2 5 // Kalmar
7 3 8 // Stockholm
8 4 10 // Värmland
Advertisement formats (ab_ads_formats)
ID adID formatID
1 1 1 // 250x360
2 1 2 // 980x120
3 2 1 // 250x360
4 3 2 // 980x120
Formats table (ab_formats)
ID name width height
1 Format 1 250 360
2 Format 2 980 120
So, I have two flash banners both are supposed to call a PHP-script which in turn is supposed to deliver an XML-file back with all the results.
I know how to select data from different tables, but I've never worked with selecting multiple rows from another table and merging them into one, which I suppose is that I need to do here. I'm very thankful for any help I can get.
The flash banners will send two parameters to the PHP file, stateID and formatID. Which means I have to SELECT ad WHERE state = param AND format = format. But since I store multiple entries for the ad states I don't know how to do it.
EDIT:
I would also like to fetch the format names in the query and get them in the following format: "Format 1,Format 2" in a column named "formats". I guess this would require some kind of join?
Thanks in advance!
I think this will work:
select ab.name as formats, aa.* from ab_ads as aa
inner join ab_ads_states as aas on aa.id = aas.adid and aas.stateId = stateIdParam
inner join ab_ads_formats as aaf on aa.id = aaf.adid and aaf.formatId = formatIdParam
inner join ab_formats as ab on aaf.formatid = ab.id
Edit:
I'm not very good with mySql, and don't have anything to test this on, but I think group_concat may be what you are looking for. If so, it will probably look something like this:
select group_concat(ab.name separator ", ") as formats from ab_ads aa
inner join ab_ads_states as aas on aa.id = aas.adid and aas.stateId = 2
inner join ab_ads_formats as aaf on aa.id = aaf.adid and aaf.formatId in(1,2)
inner join ab_formats as ab on aaf.formatid = ab.id
group by ab.id
Please try below SQL:
SELECT count(aaf.ID) AS TotalFormat,
group_concat(ab.name) AS formats
FROM ab_ads aa
INNER JOIN ab_ads_states AS aas ON aa.ID = aas.adID
AND aas.stateID = 2
INNER JOIN ab_ads_formats AS aaf ON aa.id = aaf.adID
AND aaf.formatID in(1,2)
INNER JOIN ab_formats AS ab ON aaf.formatID = ab.ID
GROUP BY aaf.adID HAVING TotalFormat >=2
SQL Demo: http://sqlfiddle.com/#!2/9f0ab/10

Categories