I am trying to show the amount of reviews for each restaurant.
I made 2 tables containing my data.
CREATE TABLE `restaurants` (
`id` int(4) NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`city` varchar(200) NOT NULL,
`country` varchar(200) NOT NULL,
`score` int(1) NOT NULL DEFAULT '0',
`reviews` int(1) NOT NULL DEFAULT '0',
`slug` varchar(200) NOT NULL DEFAULT 'slug-test',
`approved` int(1) NOT NULL DEFAULT '0',
`description` text NOT NULL,
`review` int(11) DEFAULT '0',
`created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_date` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`img_url` varchar(255) NOT NULL,
`category` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=latin1
CREATE TABLE `reviews` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`restaurant_id` int(11) NOT NULL,
`review_text` text NOT NULL,
`score` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `restaurant_id` (`restaurant_id`),
CONSTRAINT `reviews_ibfk_1` FOREIGN KEY (`restaurant_id`) REFERENCES `restaurants` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
I am already showing my restaurants in a list.
$query = "SELECT * FROM restaurants WHERE approved = 1 ORDER BY created_date DESC";
$result = $mysqli->query($query);
while($row = $result->fetch_array()) {
**HTML WITH MY VARIABLES**
}
This is how u currently show my reviews (hardcoded result in my restaurants table.
if ($reviews <= "0") {
echo "<a href='#' title='Write reviews'><p class='purple-def-color fz-12 mb-0'>write a review</p></a>";
} else if ($reviews > "1") {
echo "<p class='purple-def-color mb-0'>". $row['reviews'] ." reviews</p>";
} else {
echo "<p class='purple-def-color mb-0'>" . $row['reviews'] . " review</p>";
}
I tried to use a JOIN query but was not successful to show the amount of reviews in any way.
Based on your table structure, you can get a review count for each restaurant by joining the reviews table, selecting the COUNT of reviews, and grouping by restaurant ID.
Grouping by restaurant allows you to get an aggregate (e.g. count) of the joined review records for each restaurant.
SELECT
rs.*,
COUNT(rv.`id`) as `reviewCount`
FROM `restaurants` rs
LEFT JOIN `reviews` rv
ON (rv.`restaurant_id` = rs.`id`)
WHERE rs.`approved` = 1
GROUP BY rs.`id`
ORDER BY rs.`created_date` DESC;
Then, when you fetch the rows in PHP, you can reference each row's review count:
echo $row['reviewCount'];
To further demonstrate aggregate functions, here's an example of how to select the average, minimum, and maximum review score for each restaurant:
SELECT
rs.*,
COUNT(rv.`id`) as `reviewCount`,
AVG(rv.`score`) as `reviewAverageScore`,
MIN(rv.`score`) as `reviewMinScore`,
MAX(rv.`score`) as `reviewMaxScore`
FROM `restaurants` rs
LEFT JOIN `reviews` rv
ON (rv.`restaurant_id` = rs.`id`)
WHERE rs.`approved` = 1
GROUP BY rs.`id`
ORDER BY rs.`created_date` DESC;
Try a left join:
$query = 'SELECT * FROM restaurants LEFT JOIN reviews on reviews.restaurant_id = restaurants.id'
A left join returns all records from the left table (restaurants), and the matched records from the right table (reviews)
Related
I have a photo contest app where users can vote. I would like to select all of the contests where the logged in user has not voted yet.
So I have two tables.
The "contest" table :
CREATE TABLE `contest` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`category_id` int(11) NOT NULL,
`title` varchar(255) NOT NULL,
`desc` text NOT NULL,
`created_date` datetime NOT NULL,
`started_date` datetime NOT NULL,
`nb_user_min` int(11) NOT NULL,
`nb_photo_max` int(11) NOT NULL,
`nb_photo_per_user` int(11) NOT NULL,
`duration` int(11) DEFAULT NULL,
`status` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
The "contest_vote" table :
CREATE TABLE `contest_vote` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`pic_id` int(11) DEFAULT NULL,
`contest_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`date` datetime DEFAULT NULL,
`ip` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
So to be clear, I want to get the number (or the list) of contests where the user has not voted yet. So I have tried with a LEFT JOIN but it doesn't return the good set of result. Here it the query I have until now :
SELECT DISTINCT c.id, c.title, cv.user_id
FROM contest c
LEFT JOIN contest_vote cv
ON cv.contest_id = c.id AND cv.user_id != ?
GROUP BY contest_id
("?" represents the user_id parameter).
Can you help me to solve this?
This is pretty simple do with a subquery. Just grab all contest without where user is voted like this:
SELECT DISTINCT c.id, c.title
FROM contest c
WHERE c.id NOT IN (SELECT DISTINCT cv.contest_id FROM contest_vote cv WHERE cv.user_id = ?)
Try this one :
SELECT DISTINCT c.id, c.title, cv.user_id
FROM contest c
LEFT JOIN contest_vote cv ON cv.contest_id = c.id AND cv.user_id != ? WHERE c.user_id = ?
GROUP BY contest_id
these are my tables. first one is appusers table.
CREATE TABLE IF NOT EXISTS `appusers` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`email` varchar(50) NOT NULL,
`is_active` tinyint(2) NOT NULL DEFAULT '0',
`zip` varchar(20) NOT NULL,
`city` text NOT NULL,
`country` text NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=23 ;
second table is stickeruses table.
CREATE TABLE IF NOT EXISTS `stickeruses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`sticker_id` int(11) NOT NULL,
`count` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=24 ;
Third table is Devices
CREATE TABLE IF NOT EXISTS `devices` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`regid` varchar(300) NOT NULL,
`imei` varchar(50) NOT NULL,
`device_type` tinyint(2) NOT NULL,
`notification` tinyint(2) NOT NULL DEFAULT '1',
`is_active` tinyint(2) NOT NULL DEFAULT '0',
`activationcode` int(6) NOT NULL,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=28 ;
I Want to find the Sum(stickeruses.count) and COUNT(devices.id) for all appusers.
Here is my query.
SELECT `Appuser`.`id`, `Appuser`.`email`, `Appuser`.`country`, `Appuser`.`created`,
`Appuser`.`is_active`, SUM(`Stickeruse`.`count`) AS total, COUNT(`Device`.`id`)
AS tdevice
FROM `stickerapp`.`appusers` AS `Appuser`
LEFT JOIN `stickerapp`.`stickeruses` AS `Stickeruse`
ON (`Stickeruse`.`user_id`=`Appuser`.`id`)
INNER JOIN `stickerapp`.`devices` AS `Device`
ON (`Device`.`user_id`=`Appuser`.`id`)
WHERE `Appuser`.`is_active` = 1
GROUP BY `Appuser`.`id`
LIMIT 10
When I am applying each join separately the results are right, but I want to combine both joins. And when I am doing it then results are wrong. please help.
When mixing JOIN and LEFT JOIN it is a good idea to use parentheses to make it clear what your intent is.
I don't know what you need, but these syntaxes might give you different results:
FROM a LEFT JOIN ( b JOIN c ON b..c.. ) bc ON a..bc..
FROM ( a LEFT JOIN b ON a..b.. ) ab JOIN c ON ab..c..
Also, you can rearrange them do FROM a JOIN c LEFT JOIN b (plus parentheses) or any of several other arrangements. Granted, some pairs rearrangements are equivalent.
Also, beware; aggregates (such as SUM()) get inflated values when JOINing. Think of it this way: first the JOINs get all appropriate combinations of rows from the tables, then the SUM adds them up. With that in mind, see if this works better:
SELECT a.`id`, a.`email`, a.`country`, a.`created`, a.`is_active`,
( SELECT SUM(`count`)
FROM stickerapp.stickeruses
WHERE user_id = a.id
) AS total,
( SELECT COUNT(*)
FROM stickerapp.devices
WHERE user_id = a.id
) AS tdevice
FROM stickerapp.`appusers` AS a
WHERE a.`is_active` = 1
GROUP BY a.`id`
LIMIT 10
Okay so now its display results like 3 times in a row
$user_apps = mysql_query("SELECT a.name,a.download_url FROM user_apps as ua LEFT JOIN
apps as a ON (ua.app_id=a.app_id)
WHERE ua.user_id='$user_id'") or die(mysql_error());
while($raw = mysql_fetch_array($user_apps)){
$name = $raw['name'];
$url = $raw['download_url'];
echo $name;
echo "<br />";
echo $url;
}
Database Table Structure(since I am new to the site and did not know how to display the table structure I just exported the sql)
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=9 ;
CREATE TABLE IF NOT EXISTS `user_apps` (
`user_id` int(11) NOT NULL,
`app_id` int(11) NOT NULL,
KEY `user_id` (`user_id`,`app_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `apps` (
`app_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`description` text NOT NULL,
`icon` varchar(255) NOT NULL,
`download_url` varchar(255) NOT NULL,
`default` int(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`app_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
I'v tried different Join types but that does not seem to work.
Used the join query for get the result check bellow example query
$user_apps = mysql_query("SELECT DISTINCT a.name,a.download_url FROM user_apps as ua LEFT JOIN apps as a ON (ua.app_id=a.app_id) WHERE ua.user_id='$user_id'") or die(mysql_error());
while($raw = mysql_fetch_array($user_apps)){
$name = $raw['name'];
$url = $raw['download_url'];
echo $name;
echo $url;
}
change the join type as per your requirement. the above query for only example
INNER JOIN: Returns all rows when there is at least one match in BOTH
tables
LEFT JOIN: Return all rows from the left table, and the matched rows
from the right table
RIGHT JOIN: Return all rows from the right table, and the matched
rows from the left table
FULL JOIN: Return all rows when there is a match in ONE of the tables
more about join click here
AND also check this http://blog.codinghorror.com/a-visual-explanation-of-sql-joins/
You have used single quotes in query at user_id='$user_id' .
Are you sure your user_id is char, varchar or text? Just print_r($user_apps) and check it has any records or not? If user_id is int,tinyin than remove single quote.
In my database, I have 3 tables.
Jokes:
CREATE TABLE IF NOT EXISTS `jokes` (
`joke_id` int(11) NOT NULL AUTO_INCREMENT,
`joke` varchar(1024) NOT NULL,
`category_id` int(11) NOT NULL,
`vote` int(255) NOT NULL,
`date_added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`joke_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Category:
CREATE TABLE IF NOT EXISTS `category` (
`category_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(51) NOT NULL,
PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
And finally, Comments:
CREATE TABLE IF NOT EXISTS `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(40) NOT NULL,
`comment` text NOT NULL,
`joke_id` int(11) NOT NULL,
`post_id` int(11) NOT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
This function is stored in my read controller, with the name of Joke and it grabs the data from the db then sends it to the view.
public function joke($joke_id = FALSE)
{
if ($joke_id === FALSE) redirect('main'); //go to default controller
$data['results'] = $this->comments_m->getComments($joke_id, $this->uri->segment(2));
$this->load->view('template/header');
$this->load->view('template/sidebar');
$this->load->view('content/read', $data);
$this->load->view('template/footer');
}
The model (getjokes_m) has a function called readJokes which gets the joke, joke_id, category name, votes, comments. This function is used to read specific jokes in the database.
function readJokes($joke_id)
{
$query = $this->db->query("SELECT c.name, j.*, co.* FROM jokes j LEFT JOIN category c ON c.category_id = j.category_id LEFT JOIN comments co ON co.joke_id = j.joke_id WHERE j.joke_id = '$joke_id'") or die("No results found" );
//displays the results
return $query->result();
}
The query above is where the problem lies. For the moment it returns the joke as many times as the joke has been commented. I.e if the joke has 6 comments, the joke will show 6 times (no idea why). Any help altering this query, to just show the joke once, with all the comments the joke has, will be much appreciated.
You want a group by with group_concat() for the comments:
SELECT c.name, j.*, group_concat(co.comment separator '|') as comments
FROM jokes j LEFT JOIN
category c
ON c.category_id = j.category_id LEFT JOIN
comments co
ON co.joke_id = j.joke_id
WHERE j.joke_id = '$joke_id'
GROUP BY j.joke_id;
Actually the group by is not strictly needed here, unless you want to get the results for more than one joke.
I have these 2 queries and i would like to join them into one but i am unsure of how to go about it.
Query 1:
$query = "SELECT * FROM ".$db_tbl_comics." WHERE ".$db_fld_comics_publisher."='".$pub_id."'
AND ".$db_fld_comics_active."='1' GROUP BY ".$db_fld_comics_arc;
Query 2:
$q2 = mysql_query('SELECT '.$db_fld_arcs_title.' FROM '.$db_tbl_arcs.'
WHERE '.$db_fld_arcs_id.'="'.$result[$db_fld_comics_arc].'"');
Comics Table:
CREATE TABLE IF NOT EXISTS `comics` (
`id` varchar(255) NOT NULL,
`arc` int(255) NOT NULL,
`title` varchar(255) NOT NULL,
`issue` decimal(5,1) DEFAULT NULL,
`price` decimal(10,2) NOT NULL,
`plot` longtext NOT NULL,
`publisher` int(255) NOT NULL,
`isbn` varchar(255) NOT NULL,
`published` date NOT NULL,
`cover` varchar(255) NOT NULL DEFAULT './images/nopic.jpg',
`added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`views` int(255) NOT NULL DEFAULT '0',
`active` int(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `arc` (`arc`,`title`,`issue`,`publisher`)
);
Arcs Table:
CREATE TABLE IF NOT EXISTS `arcs` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`plot` longtext NOT NULL,
`added` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `title` (`title`)
);
What I need to do is get the Arcs Title from the arcs table for the respective comic arc.
You need to use INNER JOIN for that since I presume that records are present on both tables.
SELECT a.*, b.title
FROM comics a INNER JOIN arcs b
on a.id = b.id
WHERE a.Title = 'VALUEHERE'
displays all details from comics table and the title of the arc
as simple as (joining 2 queries in one, by selecting only the required field and using IN):
SELECT
'.$db_fld_arcs_title.'
FROM '.$db_tbl_arcs.'
WHERE '.$db_fld_arcs_id.' IN (
SELECT '.$db_fld_comics_arc.'
FROM '.$db_tbl_comics.'
WHERE '.$db_fld_comics_publisher.'='".$pub_id."'
AND '.$db_fld_comics_active.'='1' GROUP BY '.$db_fld_comics_arc.'
)