MySQL aggregation problems - mysql

I'm trying to count how many essays have been graded so I know how many results to display on the page. But I can't seem to get the code to work properly can someone help?
Thanks for the help in advance!
Here is what I got so far.
SELECT students.*, students_essays.*, COUNT(students_essays.id)
FROM students
INNER JOIN students_essays ON students.student_id = students_essays.student_id
INNER JOIN essays_grades ON students_essays.id = essays_grades.students_essays_id
It should look something like the code below for my pagination.
$q = "SELECT COUNT(id) FROM students_essays";
$r = mysqli_query ($mysqli, $q) or trigger_error("Query: $q\n<br />MySQL Error: " . mysqli_error($mysqli));
$row = mysqli_fetch_array ($r);
$records = $row[0];
Just in case here is my MySQL tables.
CREATE TABLE students_essays (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
student_id INT UNSIGNED NOT NULL,
content TEXT NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE students (
student_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
student_first_name VARCHAR(255) DEFAULT NULL,
student_last_name VARCHAR(255) DEFAULT NULL,
pass CHAR(40) NOT NULL,
PRIMARY KEY (student_id)
);
CREATE TABLE essays_grades (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
grade_id INT UNSIGNED NOT NULL,
students_essays_id INT UNSIGNED NOT NULL,
student_id INT UNSIGNED NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE grades (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
letter_grade VARCHAR(2) DEFAULT NULL,
grade_points FLOAT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
Here is the error message.
Error: 1140 - Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) with no GROUP columns is illegal if there is no GROUP BY clause in

Well I am guessing here based on the information at hand...
$q = "SELECT COUNT(id) FROM students_essays se INNER JOIN essays_grades eg ON se.id = eg.students_essays_id";
That would return all essays with a matching grade record.

SELECT students.*, students_essays.*, COUNT(students_essays.id)...
students.* and students_essays.* return multiple rows but COUNT(students_essays.id) would always return just one row. In my experience, MySQL returns the number of rows and the very first row only. The simplest way to do what you want might be running two separate queries: one for count and the other for fetching actual data.

Related

How to Join external customer table and internal customer table base on name

I have two customer table, internal and external both with nameļ¼Œ email address
I have another shopping list table which join these customer table by name.
how I can get the shopping list given name in one sql?
shopping item | customer name| customer email|
is there a way to join one table if not found then join another table?
DDL is like
CREATE TABLE `shoppinglist` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`customername` varchar(40) NOT NULL,
`itemname` varchar(40) NOT NULL,
)
CREATE TABLE `internalcustomer` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(40) NOT NULL,
`email` varchar(40) NOT NULL,
)
CREATE TABLE `externalcustomer` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(40) NOT NULL,
`email` varchar(40) NOT NULL,
)
Currently I have to use two sql
select list.itemname, intcustomer.name, intcustomer.email from shoppinglist list
join internalcustomer intcustomer on intcustomer.name = list.customname;
select list.itemname, extcutomer.name, extcutomer.email from shoppinglist list
join externalcustomer extcutomer on extcutomer.name = list.customname;
ps: the name should be uniq across both table
You may use UNION
select list.itemname, intcustomer.name, intcustomer.email from shoppinglist list
join internalcustomer intcustomer on intcustomer.name = list.customname
UNION
select list.itemname, extcutomer.name, extcutomer.email from shoppinglist list
join externalcustomer extcutomer on extcutomer.name = list.customname;
Please use UNION ALL if you want to allow duplicate values. Else, UNION operator selectes only distinct values

LEFT JOIN two tables so that left table data is filtered but right table data is displayed even if left table data does not exist

To allow our customers to store some of their own data along with our data structure I have created two extra tables:
CREATE TABLE external_columns
(
`id` INT(11) PRIMARY KEY NOT NULL,
`column` VARCHAR(30) NOT NULL,
`sid` INT(11) NOT NULL,
`bid` INT(11) NOT NULL,
`label` VARCHAR(30) NOT NULL,
`table` VARCHAR(30) NOT NULL,
`default` TINYTEXT NOT NULL
);
CREATE TABLE external_data
(
`id` INT(11) PRIMARY KEY NOT NULL,
`extcol_id` INT(11) NOT NULL,
`sid` INT(11) NOT NULL,
`bid` INT(11) NOT NULL,
`data` MEDIUMTEXT NOT NULL,
`row_id` INT(11) NOT NULL,
CONSTRAINT `external_data_external_columns_id_fk`
FOREIGN KEY (extcol_id) REFERENCES external_columns (id)
);
CREATE UNIQUE INDEX combinedUniqueIndex
ON external_data (extcol_id, sid, bid, row_id);
sid and bid are system values that identify the customer the data belongs to. row_id refers to the primary key of table referenced in table.
To get data for a certain row I have created this prepared statement:
SELECT `data`.*, `columns`.`column`, `columns`.`default`
FROM `external_columns` as `columns`
LEFT JOIN `external_data` as `data`
ON `columns`.`id` = `data`.`extcol_id`
WHERE (
`columns`.`sid` = :sid
AND `columns`.`bid` = :bid
AND `data`.`row_id` = :row_id
AND `columns`.`table` = :tableName
)
This works fine as long as for each external_column there is an entry in external_data for the given :row_id. But I want to make sure that there is always a row for each column, even if there is no data for the given :row_id. Is there a way to do this with one query?
Very close, by placing AND data.row_id = :row_id in your WHERE, you have effectively written an INNER JOIN as nulled data.row_ids won't match.
You should move this condition to the LEFT JOIN conditions:
SELECT `data`.*, `columns`.`column`, `columns`.`default`
FROM `external_columns` as `columns`
LEFT JOIN `external_data` as `data`
ON `data`.`extcol_id`= `columns`.id
AND `data`.`row_id` = :row_id
WHERE `columns`.`sid` = :sid
AND `columns`.`bid` = :bid
AND `columns`.`table` = :tableName
Personal Preferences:
Don't need the WHERE parentheses and I always tend to put the table conditions for a JOIN in the JOIN conditions where applicable and JOIN table on the LHS to make indexing options more obvious..
No difference for INNER JOINs but essential for certain LEFT JOINs.

How Can I Find Last Entered Record

I have two tables: attendees & history. History table is where I post payments for all the attendees. It also stores an historyid and the attendeeid. The query I'm using to try to get to the last record entered in the history table for a particular attendee is:
$stmt = $db->prepare('SELECT a.fname, a.lname, h.amount, h.subsidy, h.last_payment, h.balance
FROM history AS h
INNER JOIN
attendees AS a
ON a.attendeeid = h.attendeeid
WHERE a.attendeeid = :id
ORDER BY historyid DESC LIMIT 1)');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$result = $stmt->fetchAll();
But the code is failing to return anything at all. Since there will likely be historyid's greater than the one I'm trying to retrieve, how do I associate the greatest historyid for that attendee so I get the last record entered for them in the History table? What I'm using is obviously not the answer. Thanks in advance for your help.
Sorry. The schemas are:
TABLE attendees (
attendeeid int(10) unsigned NOT NULL AUTO_INCREMENT,
fname varchar(20) NOT NULL,
lname varchar(20) NOT NULL,
dojid varchar(10) NOT NULL,
address1 varchar(25) NOT NULL,
address2 varchar(25) NOT NULL,
city varchar(20) NOT NULL,
state char(2) NOT NULL,
zipcode varchar(5) NOT NULL,
phonenumber varchar(15) NOT NULL,
memberid int(10) unsigned NOT NULL,
PRIMARY KEY (attendeeid),
KEY memberid (memberid),
CONSTRAINT attendees_ibfk_2 FOREIGN KEY (memberid) REFERENCES members (memberid) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
TABLE history (
historyid int(10) unsigned NOT NULL AUTO_INCREMENT,
amount float NOT NULL,
subsidy char(1) NOT NULL,
last_payment date NOT NULL,
amount_paid float NOT NULL,
balance float NOT NULL,
attendeeid int(10) unsigned NOT NULL,
memberid int(10) unsigned NOT NULL,
PRIMARY KEY (historyid),
KEY attendeeid (attendeeid),
CONSTRAINT history_ibfk_2 FOREIGN KEY (attendeeid) REFERENCES attendees (attendeeid) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Please can you provide the structure of both tables? I'm guessing your tables contains a date columns, or something like that.
Anyway, I would try something like:
select *
from (select a.attendeeid,max(h.id)
FROM history AS h INNER JOIN attendees AS a ON a.attendeeid = h.attendeeid
group by a.attendeeid) as maxHistoryPerAttendee
In that way, you will have the maximun history id per each attendeeid. I'm guessing the max id, is the last inserted row.
This is your query with a couple fields added:
select *
from (select a.attendeeid,max(h.historyid),fname,lname,last_payment,amount
FROM history AS h INNER JOIN attendees AS a ON a.attendeeid = h.attendeeid
group by a.attendeeid) as maxHistoryPerAttendee
where attendeeid = 29
In doing that, I got the first-row entry for that attendee's last_payment instead of the last_payment associated with the MAX(historyid) row.
To add to this and call it done, I got this to work. It's very near my original query.
$stmt = $db->prepare('SELECT fname, lname, amount, subsidy, last_payment, balance, a.attendeeid, h.historyid
FROM history AS h
INNER JOIN attendees AS a ON a.attendeeid = h.attendeeid
where h.attendeeid = :id
ORDER BY h.historyid DESC LIMIT 1');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
Works like a champ!

Counting number of files in a folder in mysql

I have these tables
Create table series (
id_series Int NOT NULL AUTO_INCREMENT,
name_series Varchar(100) NOT NULL,
kcal Int NOT NULL DEFAULT 0,
type Varchar(20) NOT NULL,
id_user Int NOT NULL,
UNIQUE (name_excercise),
Primary Key (id_series)) ENGINE = MyISAM;
Create table excercise (
id_excercise Int NOT NULL AUTO_INCREMENT,
name_excercise Char(100) NOT NULL,
date Date NOT NULL,
start Time NOT NULL,
end Time NOT NULL,
km Double(10,2) NOT NULL,
id_series Int NOT NULL,
UNIQUE (id_excercise),
Primary Key (id_excercise)) ENGINE = MyISAM;
They are basically like folders. Series has a name of a series and inside, there are subsequent excercises. What I need is to return every series user has and the number of the excercises inside. But I am having trouble with it, since I am not passing any ID of a concrete series in which it could count all the excercises. Is it possible to print that with only a user id?
I basically have this:
"SELECT * FROM series WHERE id_user = $id_user"
but need to combine it with this:
"SELECT COUNT(*) FROM excercise WHERE id_series = $id_series"
without the need of id_series.
This is the query:
SELECT S.*, COUNT(E.id_excercise) AS NumOfExc
FROM series AS S
JOIN excercise AS E
ON S.id_series = E.id_series
WHERE S.id_user = '$id_user'
GROUP BY E.id_series
UNION
SELECT *, 0 AS NumOfExc
FROM series
WHERE id_user = '$id_user'
AND id_series NOT IN (
SELECT S.id_series AS id_series
FROM series AS S
JOIN excercise AS E
ON S.id_series = E.id_series
WHERE S.id_user = '$id_user'
GROUP BY E.id_series
)
The above query will show what you need.

How to INNER JOIN around a loop of tables

I have four tables as follows:
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `categories_friends` (
`category_id` int(10) unsigned NOT NULL,
`friend_id` int(10) unsigned NOT NULL,
UNIQUE KEY `category_id` (`friend_id`,`category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `friends` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`friend_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`,`friend_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `ratings` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`category_id` int(10) unsigned NOT NULL,
`title` varchar(255) NOT NULL,
`description` text NOT NULL,
`rating` tinyint(2) unsigned NOT NULL,
`public` tinyint(1) NOT NULL DEFAULT '0',
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
I am trying to perform the following query on those tables:
SELECT *
FROM `favred`.`ratings` AS `Rating`
INNER JOIN `favred`.`friends` AS `JFriend`
ON (`JFriend`.`friend_id` = `Rating`.`user_id`)
INNER JOIN `favred`.`categories_friends` AS `JCategoriesFriend`
ON (`JCategoriesFriend`.`category_id` = `Rating`.`category_id`
AND `JCategoriesFriend`.`friend_id` = `JFriend`.`id`)
INNER JOIN `favred`.`categories` AS `JCategory`
ON (`JCategory`.`id` = `Rating`.`category_id`
AND `JCategory`.`id` = `JCategoriesFriend`.`category_id`)
WHERE `JFriend`.`user_id` = 1
AND `Rating`.`user_id` <> 1
AND `JCategory`.`id` IN (4, 14)
GROUP BY `Rating`.`id`
The query above is not working, as it returns no results (although there is data in the tables that should return), what I'm trying to do is to find all the Ratings that were not authored by me (ID:1), but were authored by my Friends, but only if I've selected to view a specific Category for that Friend, with the resulting set being filtered by a given set of specific Categories.
The INNER JOINs loop around through Rating --> Friend --> CategoriesFreind --> Category --> back to Rating.
If I remove the additional portion of the INNER JOIN's ON clauses as follows:
SELECT *
FROM `favred`.`ratings` AS `Rating`
INNER JOIN `favred`.`friends` AS `JFriend`
ON (`JFriend`.`friend_id` = `Rating`.`user_id`)
INNER JOIN `favred`.`categories_friends` AS `JCategoriesFriend`
ON (`JCategoriesFriend`.`friend_id` = `JFriend`.`id`)
INNER JOIN `favred`.`categories` AS `JCategory`
ON (`JCategory`.`id` = `JCategoriesFriend`.`category_id`)
WHERE `JFriend`.`user_id` = 1
AND `Rating`.`user_id` <> 1
AND `JCategory`.`id` IN (4, 14)
GROUP BY `Rating`.`id`
then the query will return results, but because the INNER JOIN joining the CategoriesFriend to the Rating is not being filtered by the 'JCategory'.'id' IN (4, 14) clause, it returns all Ratings by that friend instead of filtered as it should be.
Any suggestions on how to modify my query to get it to pull the filtered results?
And I'm using CakePHP, so a query that would fit into it's unique query format would be preferred although not required.
first ,why are you use the JFriend.id, does it mean something,or is it as the same as user_id?
try this one,the same logic but it's from top to bottom ,I feel:
SELECT * FROM categories as JCategory
INNER JOIN categories_friends as JCategoriesFriend ON JCategoriesFriend.category_id = JCategory.id
INNER JOIN friends AS JFriend ON JFriend.friend_id = JCategoriesFriend.friend_id
INNER JOIN ratings AS Rating ON Rating.user_id = JFriend.friend_id
WHERE JCategory.id IN (4,14) AND JFriend.user_id = 1 AND Rating.user_id <> 1 GROUP BY Rating.id
I got one result from all the data that I made for the testing.
if it does not work also,try make some correct data,maybe the data is not right...
the testing data below:
categories: id | name (14| 141414)
categories_friends: category_id| friend_id (14| 2)
friends: id | user_id | friend_id (4| 1| 2)
ratings: id | user_id | category_id | title (2| 2| 14 | 'haha')
So I wondered if the INNER JOINs were being a little too limiting and specific in their ON clauses. So I thought that maybe a LEFT JOIN would work better...
SELECT *
FROM `favred`.`ratings` AS `Rating`
INNER JOIN `favred`.`friends` AS `JFriend`
ON (`JFriend`.`friend_id` = `Rating`.`user_id`)
LEFT JOIN `favred`.`categories_friends` AS `JCategoriesFriend`
ON (`JCategoriesFriend`.`friend_id` = `JFriend`.`id`
AND `JCategoriesFriend`.`category_id` = `Rating`.`category_id`)
WHERE `JFriend`.`user_id` = 1
AND `JRatingsUser`.`id` IS NULL
AND `Rating`.`user_id` <> 1
GROUP BY `Rating`.`id`
That query worked for me.
I did away with linking to the categories table directly, and linked indirectly through the categories_friends table which sped up the query a little bit, and everything is working great.