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
Related
In my database i have created two tables , the one is "categories" and the other "click_count".
The two tables have the following information : categories( cat_id, cat_name , cat_description ) and click_count(id, cat_id, cat_count). I have already written a php code which echo a table with information about categories and i have already written a php code whick calculates the click counts, so i want a php script which i can echo on the same table the information about click_count and specify the "cat_count" which contains the number about "clicks" . The following code is obviously wrong but you can get the logic.
<?php
$sql4 = "SELECT categories.cat_id,categories.cat_name,click_count.cat_id,click_count.cat_count WHERE categories.cat_id=click_count.cat_id";
$result4 = mysqli_query($conn, $sql4);
$row4 = mysqli_fetch_array($result4);
while($row4 = mysqli_fetch_assoc($result4)) {
echo '<td>'.$row4['cat_count']; }?>
The SELECT statement should contain a JOIN:
SELECT categories.cat_id, categories.cat_name, click_count.cat_id, click_count.cat_count
FROM categories
LEFT JOIN click_count
ON categories.cat_id = click_count.cat_id;
...and you can also add a WHERE clause at the end if you need it to select not all, but only the ones that fit a certain condition.
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 two tables in my database.
cat - catid, catname
articles - id, catid, content
so what i want to display is category name (catname) and how many articles are there in that category.
This is my code but it dosent work.
$query = "SELECT cat.cname, COUNT(articles.cat_id)".
"FROM cat, articles ".
"GROUP BY cat_id";
$result = mysql_query($query) or die(mysql_error());
while($row = mysql_fetch_array($result)){
echo $row['cname']. " - ". $row['COUNT(cat_id)'];
echo "<br />";
}
Any help will be most aprriceated. thanks.
So this is a 1:many relationship. i.e. 1 Category -> Many articles.
The best way to do this is to create a third table, an adjacency list.
Keep your 'category_id' and 'article_id' unique in tables 'cat' and 'article'.
In your third table you define the 1 : many relationships.
Table 3: cat_articles
adj_id cat_id art_id
1 1 1
2 1 2
3 2 3
4 2 4
5 3 5
Now join the tables:
$sql = "SELECT * FROM `cat_articles` adj ".
"LEFT JOIN (`cat` cat, `articles` art)".
"ON (cat.cat_id = adj.cat_id AND art.art_id = adj.art_id) ";
This takes the adjacency table, preserves it's format(due to left join) and appends the tables article and category to it, giving you a categorised table of all your articles. You can now use mysql_fetch_array() to get your results.
Edit: reference first comment, displaying number of rows
You can either, as you have done, use SQL's function COUNT to return a count of a specific column.
Or, with PHP, run the query, and then use mysql_num_rows($result) to return the number of rows SQL has in its buffer.
Alternatively, retrieve results using mysql_fetch_array($result) and use count to return the number of paired values in the array.
Using mysql_fetch_array:
$query = "SELECT cat.cname, COUNT(articles.cat_id)".
"FROM cat, articles ".
"GROUP BY cat_id";
$result = mysql_query($query) or die(mysql_error());
while($row = mysql_fetch_array($result)){
echo $row[0]. " - ". $row[1];
echo "<br />";
}
mysql_fetch_array returns a number indexed array (0,1,2,3,4, ..)
SELECT cat.cname, COUNT(articles.cat_id) artcount
FROM cat, articles WHERE c.cat_id = articles.cat_id
GROUP BY cat.cat_id
I guess you are missing the join clause. Unless you are joining the tables correctly you are creating a "cartesian product".
Get the rows "$row['cname'] and $row['artcount'].
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();
?>
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.