Here is my code:
<?php
$data = mysql_query("SELECT * FROM board") or die(mysql_error());
while($info = mysql_fetch_assoc( $data ))
{
if(!empty($info['user'])){
Print "".$info['user'].""; }
else {
}
myOtherQuery($info['id']);
}
function myOtherQuery($id) {
$result3 = mysql_query("SELECT COUNT(source_user_id) FROM likes
INNER JOIN pins ON pins.id = likes.pin_id
WHERE pins.board_id='$id'");
$c = mysql_result($result3, 0); // Cumulative tally of likes for board
{
Print "$c";
}
}
?>
The first part gets a users name and board details (board as in a photo album).
the second part joins that data with another sql table that counts the number of likes that board has.
Both are displayed as a name and a score represented by a number.
By default they are ordered by the date of creation. I'd like to be able to order them by the score. However, since the score is determined in the second part of the code, I don't know how to achieve it. Is it possible?
The solution is of course to query both at once in the first place, via a LEFT JOIN against a subquery returning the count per board_id:
SELECT
board.*,
/* Your PHP code will retrieve the likes count via this alias `numlikes` as in $info['numlikes'] */
numlikes
FROM
board
LEFT JOIN (
/* Subquery returns count per board_id */
SELECT pins.board_id, COUNT(source_user_id) AS numlikes
FROM
likes
INNER JOIN pins ON pins.id = likes.pin_id
GROUP BY pins.board_id
) likes ON board.id = likes.board_id
ORDER BY numlikes
It is nearly always significantly more efficient to perform a single query rather than n queries in a loop. You should strive to do so whenever possible.
You can do it in one query
SELECT board.*, count(likes.source_user_id) as score
FROM board
INNER JOIN pins
ON pins.board_id = board.id
INNER JOIN likes
ON pins.id = likes.pin_id
ORDER BY score
Related
I am trying to get the results of two different SUMs in one query because I am using a foreach to display the results. Actually the second result needs to be a count of how many records in the database points.PID = 3. The only way I can get the $total SUM and $conts to display properly for each result is to use a ForEach loop, but I'm not sure it is possible to have two SUM results returned, especially since the second SUM/count "conts" has a WHERE clause.
The code below works great for getting total, but as soon as I add:
SUM(points.PID WHERE points.PID = 3) AS conts
I get no results. Of course it should be count and not SUM anyway. Is this possible? I can't figure out the approach. If I need a second query for conts, how would the foreach know to match those results with the correct record in the loop?
$results = $dbh->prepare("select
wp_users.ID,
wp_users.display_name,
points.ID,
points.PID,
SUM(points.PID) AS total,
SUM(points.PID WHERE points.PID = 3) AS conts
FROM points
LEFT JOIN wp_users ON points.ID=wp_users.ID
GROUP BY points.ID ORDER BY total DESC
LIMIT 5");
$results->execute();
$row = $results->fetchAll(PDO::FETCH_ASSOC);
//Display Results
foreach ($row as $all) {
echo "<td>$all7[total]</td>";
echo "<td>$all7[conts]</td>";
You could try SUM(IF(points.PID = 3, 1, 0)) AS conts
If you perform the sum(points.PID) where points.PID = 3 as a subquery, you should be able to accomplish this. Your query would end up being something like this:
SELECT
wp_users.ID,
wp_users.display_name,
points.ID,
points.PID,
SUM(points.PID) AS total,
cnts.tot as conts
FROM points
INNER JOIN (
SELECT
SUM(points.PID) as tot,
ID
FROM
points
) cnts
ON cnts.ID = points.ID
LEFT JOIN wp_users
ON points.ID = wp_users.ID
GROUP BY points.ID
ORDER BY total DESC
LIMIT 5
I'm loading comments for product with id = '3'
$get_comments = mysql_query("SELECT * FROM products_comments WHERE product_id = '3'");
Now I want to add the "report abuse" option for each comment, for this purpose I'm having another table as "abuse_reports" which user abuse reports will be stored in this table, now if a user reported a comment, the report abuse option should not be there for that comment for that user there anymore, for this I'm doing:
while($row = mysql_fetch_array($get_comments)){
echo blah blah blah // comment details
// now for checking if this user should be able to report this or not, i make this query again:
$check_report_status = mysql_query("SELECT COUNT(id) FROM abuse_reports WHERE reporter_user_id = '$this_user_id' AND product_id = 'this_product_id'");
// blah blah count the abuse reports which the current user made for this product
if($count == 0) echo "<a>report abuse</a>";
}
With the above code, for each comment I'm making a new query, and that's obviously wrong, how I should join the second query with the first one?
Thanks
Updated query (that is working now, commited by questioner)
SELECT pc. * , count( ar.`id` ) AS `abuse_count`
FROM `products_comments` pc
LEFT OUTER JOIN `abuse_reports` ar ON pc.`id` = ar.`section_details`
AND ar.`reporter_id` = '$user_id'
WHERE pc.`product_id` = '$product_id'
GROUP BY pc.`id`
LIMIT 0 , 30
The query works as follow: You select all the fields of your products_comments with the given product_id but you also count the entries of abuse_reports for the given product_id. Now you LEFT JOIN the abuse_reports, which means that you access that table and hang it on to the left (your products_comments table). The OUTER allows that there is no need for a value in the abuse_reports table, so if there is no report you get null, and therefore a count of 0.
Please read this:
However, I needed to group the results, otherwise you get only one merged row as result. So please extend your products_comments with a field comment_id of type int that is the primary key and has auto_increment.
UPDATE: abuse count
Now you can do two things: By looping through the results, you can see for each single element if it has been reported by that user or not (that way you can hide abuse report links for example). If you want the overall number of reports, you just increase a counter variable which you declare outside the loop. Like this:
$abuse_counter = 0;
while($row = mysql....)
{
$abuse_counter += intval($row['abuse_count']); // this is 1 or 0
// do whatever else with that result row
}
echo 'The amount of reports: '.$abuse_counter;
Just a primitive sample
I believe your looking for a query something like this.
SELECT pc.*, COUNT(ar.*)
FROM products_comments AS pc
LEFT JOIN abuse_reports AS ar ON reporter_user_id = pc.user_id AND ar.product_id = pc.product_id
WHERE product_id = '3'"
try this SQL
SELECT pc.*, COUNT(ar.id) AS abuse_count
FROM products_comments pc
LEFT JOIN abuse_reports ar ON pc.product_id = ar.product_id
WHERE pc.product_id = '3' AND ar.reporter_user_id = '$this_user_id'
GROUP BY pc.product_id
The result is list of products_comments with abuse_reports count if exist for reporter_user_id
$sql = "SELECT * FROM books LEFT JOIN users
ON books.readby=users.user_id WHERE users.email IS NOT NULL";
$result = mysql_query($sql);
while($row = mysql_fetch_array($result))
{
echo $row['readby']. " - read 10 books";
} //while ends
this is the code I have so far. I am trying to retrieve the number of books read by each user
and echo the results. echo the user_id and number of books he/she read
books table is like this : id - name - pages - readby
the row readby contains the user id.any ideas/suggestions? I was thinking about using count() but Im not sure how to go about doing that.
A subquery can return the count of books read per user. That is left-joined back against the main table to retrieve the other columns about each user.
Edit The GROUP BY had been omitted...
SELECT
users.*,
usersread.numread
FROM
users
/* join all user details against count of books read */
LEFT JOIN (
/* Retrieve user_id (via readby) and count from the books table */
SELECT
readby,
COUNT(*) AS numread
FROM books
GROUP BY readby
) usersread ON users.user_id = usersread.readby
In your PHP then, you can retrieve $row['numread'] after fetching the result.
// Assuming you already executed the query above and checked errors...
while($row = mysql_fetch_array($result))
{
// don't know the contents of your users table, but assuming there's a
// users.name column I used 'name' here...
echo "{$row['name']} read {$row['numread']} books.";
}
You can use count() this way:
<?php
$count = mysql_fetch_array(mysql_query("SELECT COUNT(`user_id`) FROM books LEFT JOIN users ON books.readby=users.user_id WHERE users.email IS NOT NULL GROUP BY `user_id`"));
$count = $count[0];
?>
Hope this helps! :)
i am having trouble creating a single mysql query for what i am trying to do here.
first off, i will show you the table structures and fields of the tables i am using for this particular query:
users:
- id
- name
- photo_name
- photo_ext
user_attacks:
- id
- level
user_news_feed:
- id
- id_user
- id_status
- id_attack
- id_profile
- id_wall
- the_date
user_status:
- id
- status
user_wall:
- id
- id_user
- id_poster
- post
whenever the user posts an attack, or status update, updates their profile, or posts on someones wall, it inserts the relevant data into its respective table and also inserts a new row into the user_news_feed table.
now, what i want to do is select the last 10 news feed items from the database. these news feed items need to grab relevant data from other tables as long as their value is not 0. so if the news feed is for a status update, the id_status would be the id of the status update in the user_status table, and the "status" would be the data needing to be selected via a left join. hope that makes sense.
heres my first mysql query:
$sql = mysql_query("select n.id_user, n.id_status, n.id_attack, n.id_profile, n.id_wall, n.the_date, u.id, u.name, u.photo_name, u.photo_ext, s.status
from `user_news_feed` as n
left join `users` u on (u.id = n.id_user)
left join `user_status` s on (s.id = n.id_status)
where n.id_user='".$_GET['id']."'
order by n.id desc
limit 10
");
now this works great, except for 1 problem. as you can see the user_wall table contains the id's for 2 different users. id_user is the user id the post is being made for, and id_poster is the user id of the person making that wall post. if the user makes a wall post on his/her own wall, it is inserted into the database as a status update into the user_status table instead.
so i have a conditional statement within the while loop for the first query, which has another sql query within it. here is the whole code for the while loop and second sql query:
while ($row = mysql_fetch_assoc($sql))
{
if ($row['id_wall'] != 0)
{
$sql_u = mysql_query("select u.id, u.name, u.photo_name, u.photo_ext, w.post
from `user_wall` as w
left join `users` u on (u.id = w.id_poster)
where w.id='".$row['id_wall']."'
");
while ($row_u = mysql_fetch_assoc($sql_u))
{
$row['photo_name'] = $row_u['photo_name'];
$row['photo_ext'] = $row_u['photo_ext'];
$row['id_user'] = $row_u['id'];
$row['name'] = $row_u['name'];
$content = $row_u['post'];
}
}
else
{
if ($row['id_status'] != 0)
$content = $row['status'];
else if ($row['id_attack'] != 0)
$content = '<i>Had an attack</i>';
else if ($row['id_profile'] != 0)
$content = '<i>Updated profile</i>';
}
echo '<li'.(($count == $total_count) ? ' class="last"' : '').'>';
echo '<img src="images/profile/'.$row['photo_name'].'_thumb.'.$row['photo_ext'].'" alt="" />';
echo '<div class="content">';
echo '<b>'.$row['name'].'</b>';
echo '<span>'.$content.'</span>';
echo '<small>'.date('F j, Y \a\t g:ia', $row['the_date']).'</small>';
echo '</div>';
echo '<div style="clear: both;"></div>';
echo '</li>';
}
i hope what i am trying to do here makes sense. so basically i want to have both sql queries ($sql, and $sql_u) combined into a single query so i do not have to query the database every single time when the user_news_feed item is a wall post.
any help would be greatly appreciated and i apologise if this is confusing.
SELECT n.id_user, n.id_status, n.id_attack, n.id_profile, n.id_wall, n.the_date,
u.id, u.name, u.photo_name, u.photo_ext, s.status,
w.id AS wall_user_id, w.name AS wall_user_name,
w.photo_name AS wall_user_photo_name,
w.photo_ext AS wall_user_photo_ext,
w.post
FROM user_news_feed AS n
LEFT JOIN users AS u ON (u.id = n.id_user)
LEFT JOIN user_status s ON (s.id = n.id_status)
LEFT JOIN (SELECT a.id AS id_wall, b.id, b.name, b.photo_name, b.photo_ext, a.post
FROM user_wall AS a
LEFT JOIN users AS b ON (b.id = a.id_poster)
) AS w ON w.id_wall = n.id_wall
WHERE n.id_user = ?
ORDER BY n.id desc
LIMIT 10
The '?' is a placeholder where you can provide the value of $_GET['id'].
Basically, this adds an extra outer join, to the main query (and some extra columns, which will be NULL if the news feed event is not a wall posting), but the outer join is itself the result of an outer join.
Back again ;)
Anyway, forget about merging the queries in my opinion.
What you should do instead is to do the first query, loop through all the results and store all "id_wall"s in a separate array... then rather than doing a separate query per "id_wall" you do this:
$wallids = array();
while ($row = mysql_fetch_assoc($sql))
{
$wallids[] = $row['id_wall'];
// also store the row wherever you want
}
$sql_u = mysql_query("select u.id, u.name, u.photo_name, u.photo_ext, w.post
from `user_wall` as w
left join `users` u on (u.id = w.id_poster)
where w.id IN ('".implode(',', $wallids) ."')
");
$wallids being an array with all the "id_wall"s. So now you have a total of 2 queries.
I want multiple photos and multiple videos, the main problem is that I can't get them inline if I don't use joins.
So for example, I get 2 photos a video and again a photo.
I have a parent news table and 2 secondary table news_photos and news_videos and I want to get in one query the photos and videos for the news.
Is this somehow possible?
mysql_query("
SELECT *
FROM news_photos, news_videos
FULL JOIN news_videos
ON news_id = {$news_id}
FULL JOIN news_photos
ON news_id = {$news_id}
");
An image about the structure:
There's actually only a single FULL JOIN in that, since you are not involving the news table at all.
SELECT *
FROM news_photos
FULL JOIN news_videos
ON news_photos.news_id=news_videos.news_id
WHERE news_photos.news_id=... OR news_videos.news_id=...
FULL JOIN is not supported by MySQL. It can be less-efficiently simulated using two LEFT JOINs and a UNION, but it's relatively rare that you actually need to. Assuming every photo and video does belong to a news, you could avoid it and get a more conventional query by bringing the news table into it:
SELECT *
FROM news
LEFT JOIN news_photos ON news_photos.news_id=news.id
LEFT JOIN news_videos ON news_videos.news_id=news.id
WHERE news_id=...
But still, this is almost certainly not what you mean! If there are multiple photos and videos for a news item, you would be effectively creating a cartesian product, where every combination of photo and video produces a row. This is the sort of combinatorial explosion you almost never want!
If you just want one of each photo and video, I suppose you could hack that into a single query using a LEFT JOIN that will always give NULL on the other side:
SELECT * FROM news_photos
LEFT JOIN news_videos ON 0
WHERE news_photos.news_id=...
UNION SELECT * FROM news_photos
RIGHT JOIN news_videos ON 0
WHERE news_videos.news_id=...
But there's really nothing to be gained by this. Don't shoehorn two separate queries (“I'd like the photos for a news, and the videos for a news”) into one. Just do it the trivial way:
SELECT * FROM news_photos
WHERE news_id=...
SELECT * FROM news_videos
WHERE news_id=...
i would do it using a stored procedure that had multiple select statements as follows:
http://pastie.org/1141100
drop procedure if exists list_news_photos_videos;
delimiter #
create procedure list_news_photos_videos
(
in p_news_id int unsigned
)
proc_main:begin
select n.* from news n where n.news_id = p_news_id;
select p.* from news_photos p where p.news_id = p_news_id order by photo_id desc;
select v.* from news_videos v where v.news_id = p_news_id order by video_id desc;
end proc_main #
you would call this in mysql as follows:
call list_news_photos_videos(2);
then you can call the stored procedure from php (1 db call only) using mysqli as follows:
http://pastie.org/1141103
<?php
// quick and dirty demo - needs to be made more robust !!
$db = new Mysqli("localhost", "foo_dbo", "pass", "foo_db");
$sql = sprintf("call list_news_photos_videos(%d)", 2); // get all the news related data in one query
$result = $db->query($sql);
//news item
$row = $result->fetch_assoc();
echo sprintf("<h2>news item</h2>news_id = %d subject = %s <br/>", $row["news_id"], $row["subject"]);
$result->free();
//news photos
$db->next_result();
$result = $db->use_result();
echo "<h2>news photos</h2>";
while ($row = $result->fetch_assoc()){
echo sprintf("photo_id = %d subject = %s<br/>", $row["photo_id"], $row["subject"]);
}
$result->free();
//news videos
$db->next_result();
$result = $db->use_result();
echo "<h2>news videos</h2>";
while ($row = $result->fetch_assoc()){
echo sprintf("video_id = %d subject = %s<br/>", $row["video_id"], $row["subject"]);
}
$result->free();
$db->close();
?>