MySQL query: display all ungraded essays - mysql

So far I can list all the students essays and graded student essays and was wondering how would I be able to display all ungraded essays? What else do I have to add to my MySQL code?
Here is the code I have so far.
SELECT students.*, students_essays.*
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
ORDER BY students_essays.id DESC
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)
);

SELECT students.*, students_essays.*
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
INNER JOIN grades ON essays_grades.grade_id = grades.id
WHERE LETTER_GRADE IS NULL
ORDER BY students_essays.id DESC;
I used INNER JOIN, just like you did. Note that an inner join will hide rows in the left table, if no matching entry in the right table is found. Use left join if you want to include those as well.
I assumed that letter_grade is null if the essay is ungraded. If your database will not have a essays_grades row, if an essay is not graded, the query will be simpler:
SELECT students.*, students_essays.*
FROM students
INNER JOIN students_essays ON students.student_id = students_essays.student_id
LEFT JOIN essays_grades ON students_essays.id = essays_grades.students_essays_id
WHERE essays_grades.grade_id IS NULL
ORDER BY students_essays.id DESC;

Is ungraded a letter_grade, e. g. 'UG'?
In this case, add
WHERE letter_grade = 'UG'
before the ORDER BY.

SELECT students.*, students_essays.*
FROM students
INNER JOIN students_essays ON students.student_id = students_essays.student_id
LEFT JOIN essays_grades ON students_essays.id = essays_grades.students_essays_id
WHERE essays_grades.students_essays_id is null
ORDER BY students_essays.id DESC

Related

Inner join with 3 tables (the second and third "assigned" to the first - not all together)

I have 3 tables which I would like to "inner join" - but always to the first table!
SELECT *
FROM scene_block AS sb
INNER JOIN roles AS r ON (sb.selected_block = r.id
AND sb.block_type = 'role'
AND r.id NOT IN (21))
INNER JOIN script_actors AS sa ON (sb.selected_block = sa.id
AND sb.block_type = 'actor')
WHERE
sb.scene_id = '1'
GROUP BY
sb.id
ORDER BY
sb.position
The same query with LEFT JOIN returns all results as expected with the r.id 21 "NULL", the query with INNER JOIN returns 0 results (as there are all inner joins together)
Result Left Join
But that's not what I want to achieve...
I would like to achieve, that If on scene_block "block_type='role'" the roles table is gonna be inner joined, if block_type='actor' the script_actors is gonna be inner joined... and the link between the rows is alsways scene_block.selected_id = .id
Tables
CREATE TABLE `scene_block` (
`id` int(11) UNSIGNED NOT NULL,
`scene_id` int(11) NOT NULL,
`block_type` enum('actor','role') NOT NULL,
`selected_block` int(11) DEFAULT NULL,
`content` text NOT NULL,
`hideable` enum('0','1') NOT NULL DEFAULT '1',
`position` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `script_actors` (
`id` int(11) NOT NULL,
`script_id` int(11) NOT NULL,
`realname` varchar(200) NOT NULL,
`actorname` varchar(200) NOT NULL,
`description` text NOT NULL,
`position` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `roles` (
`id` int(11) UNSIGNED NOT NULL,
`location_id` int(11) NOT NULL,
`title` varchar(256) NOT NULL,
`color` varchar(7) NOT NULL,
`color_live` varchar(7) NOT NULL,
`position` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Without having tested it on your data I presume that you get closer to your desired result if you use INNER JOINs instead of LEFT JOINs.
SELECT *
FROM scene_block AS sb
INNER JOIN roles AS r ON (sb.selected_block = r.id
AND sb.block_type = 'role'
AND r.id NOT IN (21))
INNER JOIN script_actors AS sa ON (sb.selected_block = sa.id
AND sb.block_type = 'actor')
WHERE
sb.scene_id = '1'
GROUP BY
sb.id
ORDER BY
sb.position
Inner joins don't work here, because a scene block type cannot be 'role' and 'actor' at the same time. You need outer joins to get either the role or the actor. Then add a condition to get rid of scene blocks that have no match.
SELECT *
FROM scene_block AS sb
LEFT JOIN roles AS r ON sb.selected_block = r.id
AND sb.block_type = 'role'
AND r.id <> 21
LEFT JOIN script_actors AS sa ON sb.selected_block = sa.id
AND sb.block_type = 'actor'
WHERE sb.scene_id = 1
AND (sa.id IS NOT NULL OR r.id IS NOT NULL)
ORDER BY sb.position;

Joining different tables based on column value

I have a table called notifications:
CREATE TABLE `notifications` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`type` varchar(20) NOT NULL DEFAULT '',
`parent_id` int(11) DEFAULT NULL,
`parent_type` varchar(15) DEFAULT NULL,
`type_id` int(11) DEFAULT NULL,
`etc` NULL
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8;
Each notification is related to a different table, the value of parent_type field specifies the name of the table that I want to * join the table with. All target tables have several similar columns:
CREATE TABLE `tablename` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`is_visible` tinyint(1) NOT NULL,
`etc` NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
Currently I'm using this query for selecting notifcations that their related row in the target table exists and their is_visible field is 1:
SELECT n.id,
FROM notifications n
LEFT JOIN books b ON n.parent_id = b.id AND n.parent_type = 'book' AND b.is_visible = 1
LEFT JOIN interviews i ON n.parent_id = i.id AND n.parent_type = 'interview' AND i.is_visible = 1
LEFT JOIN other tables...
WHERE n.user_id = 1
GROUP BY n.id
But since it is a LEFT JOIN it returns the notification if it matches any table or not, how can I rewrite it so it doesn't return notifications that don't match with any row in the target table? I have also tried the CASE statement unsuccessfully.
I'm not 100% sure the syntax is right and I have no chance to test it right now, but the idea should be clear.
SELECT DISTINCT n.id
FROM notifications n
JOIN (
(SELECT b.id, 'book' AS type FROM books b WHERE b.is_visible = 1)
UNION
(SELECT i.id, 'interview' AS type FROM interviews i WHERE i.is_visible = 1)
) ids ON n.parent_id = ids.id AND n.parent_type = ids.type
WHERE n.user_id = 1

How can I build this query in mysql

Hope someone will help me to build this query. I need to get values from database which are belong to one particular user. These are the columns which I need to get.
01. city_name // From city table
02. district_name // From district table
03. category_name // From category table
04. subscription_period // From tutors table
05. date_registered // From tutors table
I have already got values tutor_id, city_id and category_id respectively 1, 53, 5
My address table has city_id column, City table has district_id column, category table has category_id column and tutor table has tutor_id and address_id.
I tried something like this, but this is not working.
SELECT city_name, district_name, category_name, subscription_period AS sp, date_registered AS date
FROM tutors AS t
INNER JOIN district AS d
INNER JOIN city ON city.district_id = d.district_id
INNER JOIN address ON address.city_id = 50
INNER JOIN category
WHERE t.tutor_id = 1 AND category.category_id= 5
My expecting result should have 1 row as output but this query give me more rows as output
My SQL Tables
CREATE TABLE tutors (
tutor_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
address_id SMALLINT NOT NULL,
tutor_name VARCHAR(80) NOT NULL,
subscription_period SMALLINT NOT NULL,
date_registered TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (tutor_id)
);
CREATE TABLE address (
address_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
address VARCHAR(40) NOT NULL,
city_id SMALLINT UNSIGNED NOT NULL,
PRIMARY KEY (address_id)
);
CREATE TABLE district(
district_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
district_name VARCHAR(30) NOT NULL,
PRIMARY KEY (district_id)
);
CREATE TABLE city(
city_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
city_name VARCHAR(30) NOT NULL,
district_id SMALLINT UNSIGNED NOT NULL,
PRIMARY KEY (city_id)
);
CREATE TABLE category (
category_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
category_name VARCHAR(60) NOT NULL,
PRIMARY KEY (category_id)
);
CREATE TABLE tutor_category(
tutor_id SMALLINT UNSIGNED NOT NULL,
category_id SMALLINT UNSIGNED NOT NULL,
PRIMARY KEY (tutor_id)
);
Try something like this:
SELECT c.city_name,
d.district_name,
c.category_name,
t.subscription_period AS sp,
t.date_registered AS date
FROM tutors t
INNER JOIN address a ON t.address_id = a.address_id
INNER JOIN city c ON c.city_id = a.city_id
INNER JOIN district d ON c.district_id = d.district_id
INNER JOIN tutor_category tc on t.tutor_id = tc.tutor_id
INNER JOIN category cat on tc.category_id = cat.category_id
WHERE t.tutor_id = 1 AND cat.category_id= 5 AND c.city_id = 50
You need to JOIN on your tables to ensure you only receive the desired results.
If you are receiving duplicate rows that are identical, you are probably doing something wrong or the definitions you provided are not correct (it appears that this is the case indeed). You could "solve" the issue by adding GROUP BY or DISTINCT to your query but that is not the best approach.

MySQL query: display only graded student essays

So far I can list all the students essays but I want to be able to display only graded student essays and was wondering how would I be able to do this? What else do I have to add to my MySQL code?
Here is what I got so far with my MySQL code.
SELECT students.*, students_essays.*
FROM students
INNER JOIN students_essays ON students.student_id = students_essays.student_id
ORDER BY students_essays.id DESC
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)
);
Just inner join to the essays_grades table (and the grades table if you want the grade).
SELECT students.*, students_essays.*
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
ORDER BY students_essays.id DESC
Here's a few tips... You can alias table names (by providing the alias after the table name in the FROM clause) and stop referring to them by their full names, and you can just say JOIN and it is implied to be an inner join.
SELECT st.*, es.*
FROM students st
JOIN students_essays es ON st.student_id = es.student_id
JOIN essays_grades gr ON es.id = gr.students_essays_id
ORDER BY es.id DESC
Just join the grades table on the essays table and check for an existing record.
SELECT students.*, students_essays.*
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
ORDER BY students_essays.id DESC

MySQL COUNT problem

Is there a way I can count how many ungraded essays there are to be displayed? IF so What else do I have to add or take away from my MySQL code?
Thanks for the help in advance!
Here is my MySQL code.
SELECT students.*, students_essays.*
FROM students
INNER JOIN students_essays ON students.student_id = students_essays.student_id
LEFT JOIN essays_grades ON students_essays.id = essays_grades.students_essays_id
WHERE essays_grades.grade_id IS NULL
ORDER BY students_essays.id DESC;
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)
);
As far as I can see you only need to look at 2 tables for this.
SELECT COUNT(*)
FROM students_essays se
WHERE NOT EXISTS(SELECT * FROM
essays_grades ge
WHERE se.id = eg.students_essays_id)
SELECT count(*)
FROM students
INNER JOIN students_essays ON students.student_id = students_essays.student_id
LEFT JOIN essays_grades ON students_essays.id = essays_grades.students_essays_id
WHERE essays_grades.grade_id IS NULL
ORDER BY students_essays.id DESC;