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();
?>
Related
I'm trying to get info from a table.
I have boards and sub-boards. The column sub_id in table board tells me whether or not the board is a sub-board or not. If it is a sub-board then the id of the parent board is the value, if it isn't then the cell is null.
I wrote this to display whether or not a board has any sub-boards:
<?php
$data = mysql_query("SELECT * FROM board WHERE sub_id='$boardId'")
or die(mysql_error());
while($info = mysql_fetch_array( $data ))
{
Print "<br>Sub-collections:<br>";
Print "<font size='5pt'><a href='/board/index/".$info['id']."'>".$info['board_name']." </a></font>";
}
?>
This works fine.
Now I want to write a similar code to name the parent board (if the board is a sub) but this is where I'm having trouble.
I came up with:
<?php
$data = mysql_query("select * from board WHERE id='$boardId' AND sub_id IS NOT NULL") or die(mysql_error());
$issub = mysql_fetch_array($data);
?>
then using...
<?php if(($issub)): ?>
<?php echo $issub['sub_id'] ?>
<?php else: ?>
<?php endif ?>
To display.
The problem is without a join I cannot show anything other than the id of the parent board. I don't know which columns to join in order to extract info like board_name etc.
EDIT:
popovitsj's suggestion:
<?php
$data = mysql_query("SELECT * FROM board b WHERE EXISTS(SELECT * FROM board WHERE b.id = sub_id)") or die(mysql_error());
while($info = mysql_fetch_array( $data ))
{
Print "<br>Sub-collection of:<br>";
Print "<font size='5pt'><a href='/board/index/".$info['id']."'>".$info['board_name']." </a></font>";
}
?>
This works to a certain extent. It correctly displays the parent board on the subboard page, but also displays it erroneously on the parent board page.
EDIT2:
After researching LEFT JOIN at Meier's suggestion I eventually got it to work using this code:
SELECT * FROM board a LEFT JOIN board b ON a.sub_id = b.id WHERE a.sub_id LIKE b.id AND a.id='$boardId'
Before I answer the question: are you 100% sure that the $boardid does not come directly from http and is therefore manipulable by the user, so your users can not start a sql injection?
The join you need is a "outer self join". Outer join because you also want to display boards that do not have a parent. Outer join is also named left join.
A self join because the table is joined to itself. The trick with the self join is to give the table different names in the statement, so you can distinguish between th parent and the child.
A quick google search for "sql outer self join" gave me this, which explains it quite nicely.
The example is with employees and managers, and the managers are also employees. So the example is quite similar to yours:
http://blog.sqlauthority.com/2010/07/08/sql-server-the-self-join-inner-join-and-outer-join/
There are different solutions to this. One possible one is:
SELECT * FROM Board b
WHERE EXISTS
(SELECT * FROM Board
WHERE b.id = sub_id);
Another possible solution could be this, this only shows 'root' parent boards. (boards with no parents themselves).
SELECT * FROM Board b
WHERE sub_id IS NULL;
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
i have a online application for wich i require a sort of dashboard (to use the white-space).
There are three tables used for the operation:
1.) categories: id, name
2.) entries: id, name, description, category_id, created, modified
3.) entryimages: id, filename, description, entry_id
on the dashboard i want to show 4-5 entries (with thumbnail images, so i require joins to the entryimages table and the categories table) for each category.
I read through some articles (and threads on s.o.) like this one:
http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
But am still not getting it right, i've tried to first extract all categories and for each and every category build a query and with "all union" attach them to one, but that is not working.
The last version of code i used:
foreach($categories as $id => $name)
{
$query .= "SELECT `entry`.`id`,
`entry`.`name`,
`entry`.`description`,
`entry`.`category_id`,
`entry`.`created`,
`entry`.`modified`,
`entryimages`.`filename`,
`entryimages`.`description`
FROM `entries` as `entry` LEFT JOIN `entryimages` ON `entryimages`.`entry_id` = `entry`.`id`
WHERE `entry`.`category_id` = $id ";
if($i < count($groups))
{
$query .= 'UNION ALL ';
}
$i++;
}
$result = mysql_query($query);
Does anybody know what is the best right to accomplish this operation?
Thanks 1000
On the dashboard if you want to show three entries, the way you are doing is wrong. If my understanding is right, the entire query will be something like
"SELECT `entry`.`id`,
`entry`.`name`,
`entry`.`description`,
`entry`.`category_id`,
`entry`.`created`,
`entry`.`modified`,
`entryimages`.`filename`,
`entryimages`.`description`
FROM `entries` as `entry`
INNER JOIN categories
ON (entry.category_id = categories.id)
LEFT JOIN (SELECT * FROM `entryimages` WHERE `entry_id` = `entry`.`id` LIMIT 1) AS `entryimages`
ON `entryimages`.`entry_id` =`entry`.`id`
ORDER BY `entry`.`created` DESC LIMIT 5";
Your code looks ok to me you should just add a LIMIT clause so that you get just five of them and an ORDER BY clause to get the latest
$query .= "SELECT `entry`.`id`,
`entry`.`name`,
`entry`.`description`,
`entry`.`category_id`,
`entry`.`created`,
`entry`.`modified`,
`entryimages`.`filename`,
`entryimages`.`description`
FROM `entries` as `entry` LEFT JOIN `entryimages` ON `entryimages`.`entry_id` = `entry`.`id`
WHERE `entry`.`category_id` = $id ORDER BY `entry`.`created` DESC LIMIT 5";
I have quite a bit of knowledge about SQL queries.
I'm trying to make gallery, and I need to select categories from table "cat_photos", which contain rows (id,name,cover,photo) and count number of photos from table "photos" which contain rows (id,thumb,photo,category).
Here is code which i use:
1) Selecting categories
$query = mysql_query("SELECT * FROM cat_photos ORDER BY ID DESC");
while($data = mysql_fetch_array($query)) {
echo "<li><a href='photos.php?cat=$data[id]'><img src='galleries/categories/$row[image]' alt='$row[name]' /></a>
<div class='photodesc'><div class='catname'><a href='photos.php?cat=$row[id]'>$row[name]</a></div>
<div class='catcount'>Number of photos in category</div></div></li>"; }
2) Counting number of photos in category
$query = mysql_query("SELECT category, COUNT(photo) FROM photos GROUP BY category") or die(mysql_error());
while($row = mysql_fetch_array($query)){
echo "Number of photos is ". $row['COUNT(photo)'] ." in cateogry ". $row['category'] .".";
echo "<br />"; }
Separated all works, but I can't find a way to merge them into one query.
I have googleing for "UNION", "JOIN", "LEFT JOIN" options in MySql query but I could't together the pieces.
I wonder if this is in general possible?
How in order that query look like?
Try this, it should work :
SELECT cat_photos.*, count(photos.id) as number_photos
FROM cat_photos
LEFT JOIN photos ON photos.category = cat_photos.id
GROUP BY cat_photos.id, cat_photos.name, cat_photos.image
ORDER BY cat_photos.id
The number of photos will be accessible trough $row['number_photos'].
Just use your second query and join the wanted category elements.
Something quick and dirty would be:
SELECT c.category, COALESCE(COUNT(p.photo),0) as photos FROM photos p, cat_photos c
WHERE c.category = p.category
GROUP BY category
Since I don't know your exact database setup just change the selected elements to the ones you really need.
//edit: Put in Coalesce to get categories with 0 photos.
Don't SELECT *. Instead select individual columns and then join:
SELECT
cat_photos_main.id, cat_photos_main.category, cat_photos_main.photodesc, cat_photos_counts.num_photos
FROM cat_photos cat_photos_main
LEFT OUTER JOIN (SELECT category, count(*) AS num_photos FROM photos GROUP BY category) cat_photos_counts
ON cat_photos_main.category = cat_photos_counts.category
I am looking for a cleaner way to do this. My code works, but I know it can be better. I have three tables: one with a list of Category Groups, One with a list of categories that are linked to category groups, and one with a list of news stories that are linked to the categories.
I need to loop through all of the names of the Category Groups, followed by the names of the categories that are in the category groups, with the number of news stories in each category listed as well.
I have three tables: CategoryGroups, Categories, News.
I have a set of queries. The first queries all the rows from the CategoryGroups table:
$result = mysql_query( '
SELECT cat_group_id, cat_group_name FROM CategoryGroups
' );
The second query is inside the looped results of the first query and finds all the categories that have a news item and are linked to a specific category group:
<?php
while( $row = mysql_fetch_assoc( $result ) ){
$id = $row['cat_group_id'];
$name = $row['cat_group_name'];
echo "<h3>$name</h3>";
$sql = mysql_query("
SELECT category_id, title FROM `Categories`
WHERE cat_group_id = $id
AND category_id IN
(SELECT news.category_id FROM news)
");
while( $row = mysql_fetch_assoc($sql) ) {
$title = $row['title'];
$catid = $row['category_id'];
$numbers = mysql_query("
SELECT * FROM news
WHERE category_id =$catid"
);
$nums = mysql_num_rows($numbers);
echo "$title ($nums)<br/>\n";
}
?>
I would like to limit this to one or two queries, with efficiency in mind. I know this can be done, however I have not been successful in my attempts.
thanks.
Why not JOIN the tables?
SELECT cat_group_name, title, count(newsid)
FROM CatagoryGroups
INNER JOIN Categories ON cat_group_id
INNER JOIN News ON category_id
GROUP BY cat_group_name, title
looks like it should be close, if table news has a newsid column (it's gotta have SOME primary key, right? well, count that;-). With the obvious indexes the JOINs should be quite fast, and your PHP code can do whatever output formatting you may need from that.
I suggest you need to get a book on SQL, such as "SQL Queries for Mere Mortals."
$sql = mysql_query("
SELECT cg.cat_group_name, c.title, COUNT(n.category_id) AS NumNews
FROM `CategoryGroups` cg
JOIN `Categories` c USING (cat_group_id)
JOIN `News` n USING (category_id)
GROUP BY cg.cat_group_name, c.title");
Then loop over the result and output a new <h3> each time the cat_group_name is different from the previous row.