views:

68

answers:

2

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: alt text

+2  A: 

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=...
bobince
Hi thanks for your answer, 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've tried your first example, I get an error what says that news_id is invalid in WHERE clause and the second one, this is works but not in the right way, for example I have 2 photos and one videos but the script adds the video to each photos so the order is `photo1, video1, photo2, video1`. I hope you understand now better what I want :)
CIRK
It sounds like you want an iterative approach to pull out all records that match. For this the easiest way would be a loop / cursor to retrieve all your records. I try to avoid the cursors but thats how Im understanding your explanation..
Wes Price
Hi @Wes Price, Yes I want to pull all `videos` and `photos` what belongs to `news` inline. But I don't really understand what you mean under `For this the easiest way would be a loop / cursor to retrieve all your records.` Without a loop I can't get multiple items from a database that's totally normal, and what are cursors?:D Sry If I misunderstood something.
CIRK
@bobince your third and fourth example separates `photos` and `videos`
CIRK
Full error on your example 1 : `Unknown column 'news_photos.news_id' in 'where clause'`;
CIRK
Well I don't know what your schema is (you didn't tell us!). If the foreign key to `news` is called something other than `news_id` like your diagram/example query implies, do substitute that name instead. If you don't want the photos and videos listed one after another, again, you'll have to tell us what column in your scheme should govern the order. Remember photos and videos are two different entities; unless there is a shared ordering column there is no consistent or sensible way to decide which should go first, and all-the-photos-then-all-the-videos is as good an order as any!
bobince
Whilst you can UNION the two result sets then ORDER them on a shared column, this is a post-processing step that it isn't going to be much more efficient to do in the database. You could sort and present it just as well in PHP, in the application's presentation layer. And if you're trying to interleave results, so there's no specific order but you want to take *one* photo then *one* video alternately, that's something that definitely should be done in the PHP. Don't force the database into behaving in an undatabasey way when you've got a full-on programming language at your fingertips!
bobince
A: 

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();

?>
f00
Hi @f00 thanks for your answer. As I see this script brings `photos` and `videos` separately but I want them inline, please read my comment on @bobince 's answer. Or if I'm wrong could you tell me how is this working?
CIRK
Cool thanks very much!
CIRK
if i have 1 news item 3 photos and 2 videos what determines the inline ordering - the date they were created ?
f00
haha :D I was thinking exactly about this right now, well I think we will need a position row too :) Date is not good because they will be created at the same time.
CIRK
But please keep in mind that I have 1 news and in that news I have 3 photos and 2 videos but not inside the news content, so for example: `<div id="news_content">Today 2000 people died in Afghanistan</div><div id="media">photo, video, photo etc...</div>`
CIRK
yes i get it now, and it's a little tricky as you have separate relations for photos and videos (media) so i might need to tweak your schema...
f00
i've devised a solution for you but i'm a little busy with other things at the moment so here's a snapshot of the database so far, will post a new answer as soon as I can get off this phone !! http://pastie.org/1141336 Make sure you understand what i've done in the backend before we proceed :)
f00
Hey thanks! I'm busy too, but you don't need to write from scratch I need only the SELECT part :P.
CIRK
no you need it all - here's the output of the php script prior to XSL transformation: http://pastie.org/1141393
f00