Database get rid of repeated attribute - mysql

So basically I have 3 tables: student, class, and enrollment.
CREATE TABLE class
(`class_name` varchar(13), `class_id` int primary key)
;
INSERT INTO class
(`class_name`, `class_id`)
VALUES
('math', 5697),
('science', 5768),
('computer', 6315),
('physical-ed', 6422),
('music', 7528),
('art', 7604),
('jrotc', 8797),
('culinary-arts', 8069)
;
CREATE TABLE student
(`student_fname` varchar(8), `student_id` varchar(11) primary key)
;
INSERT INTO student
(`student_fname`, `student_id`)
VALUES
('james', 'Vre94b3JpXO'),
('jim', 'JzqQ2zRVNm1'),
('jenny', 'xgqv9P42eYL'),
('kyle', 'QLNM0Wbyqk0'),
('kimberly', 'P2egAddWN0Q'),
('kayla', 'EGNDjWAreAy'),
('noah', 'bPeOyMMONGr'),
('nataly', '9Op53GGmqk5')
;
create table enrollment (
`no` int(10),
`student_id` varchar(11),
`class_id` int,
`semester` varchar(20),
primary key (`student_id`, `class_id`, `semester`),
foreign key (`student_id`) references student (`student_id`),
foreign key (`class_id`) references class (`class_id`)
);
insert into enrollment values
(1, 'Vre94b3JpXO', 5697, 'Fall 2015'),
(2,'JzqQ2zRVNm1', 5697, 'Fall 2015'),
(3, 'xgqv9P42eYL', 5697, 'Fall 2015'),
(4, 'JzqQ2zRVNm1', 8069, 'Fall 2014'),
(5, 'xgqv9P42eYL', 8069, 'Fall 2014');
If I follow the query:
select * from student s
join enrollment e on s.student_id = e.student_id
join class c on c.class_id = e.class_id
I would get something like this:
How could I remove the redundant column like student_id and class_id?
This question is extended from the page:
Table structure - Link one student with multiple classes id
and demo
I have been using the joining method but I couldn't get what I wanted, thanks for your help.

To answer this question, it might be nice to review some basics :
SELECT *
This * is to display all the columns from the table(s) you have selected
If you want to display only some columns, you have to specificly name it :
SELECT class_name, ...
But if you have some tables with the same column name, you have to specify the table name or alias :
SELECT c.class_name
FROM mytable t
INNER JOIN class c ON c.id_table = t.id

Just specify the columns you want to see.
select s.student_fname, s.student_id,
e.no, e.class_id, e.semester,
c.class_name,
from
student s join enrollment e
on s.student_id = e.student_id
join class c
on c.class_id = e.class_id ;

Something like this:
SELECT s.*
,e.no
,e.class_id
,e.semester
,c.class_name
FROM student s
INNER JOIN enrollment e ON s.student_id = e.student_id
INNER JOIN class c ON c.class_id = e.class_id
In your select you can specify each column you'd like and thus what order.
SELECT e.no
,e.class_id
If you want all columns from a table you can do that table dot star like:
SELECT s.*
As you already have in the existing query you can simply do star to get all the columns:
SELECT *
Here is a good place to start for some basic SELECT tricks: https://www.techrepublic.com/article/sql-basics-select-statement-options/
On an aside, it can be helpful to use a site like: http://poorsql.com/ to help with your SQL formatting.

Related

Join unrelated tables in summarized manner

I am finding a hard time to summarize the SQL table.
Objective: from the given tables I have to join and summarize the table.
col1 = Name_of_student,
col2 = Name_of_subject(where she/he scored highest),
col3= highest_number,
col4 = faculty_Name(where she/he scored highest),
col5 = Name_of_subject(where she/he scored lowest),
col6 = lowest marks,
col7 = faculty_Name(where she/he scored lowest)
Note - I have to write only one query for the given output.
There four tables:
Students.
Students_subject.
Faculty.
Marks.
You can copy the code in my SQL script for understanding the tables.
create database university ;
use university ;
create table students (id int auto_increment primary key,
student_name varchar(250) NOT NULL,
dob DATE NOT NULL) ;
create table faculty ( id int auto_increment primary key,
faculty_name varchar(250) NOT NULL,
date_of_update datetime default NOW()) ;
create table Students_subject ( id int auto_increment primary key,
subject_name varchar(250) default 'unknown' NOT NULL,
subject_faculty int not null,
foreign key(subject_faculty) references faculty(id));
create table marks (id int auto_increment primary key,
student_id int NOT NULL,
subject_id int NOT NULL,
marks int NOT NULL,
date_of_update datetime default now() ON UPDATE NOW(),
foreign key(student_id) references students(id),
foreign key(subject_id) references students_subject(id));
insert into students ( student_name, dob) values
('rob', '2001-03-06'),
('bbb', '2001-09-06'),
('rab', '1991-03-06'),
('root', '2001-03-16') ;
insert into faculty(faculty_name) values
('kaka'),
('dope'),
('kallie'),
('kim');
insert into students_subject (subject_name, subject_faculty) values
('maths', 2),
('physics', 3),
('english', 4),
('biology', 1),
('statistics', 2),
('french', 4),
('economics',3);
insert into marks ( student_id, subject_id, marks) values
(1,1,70),
(1,2,60),
(1,3,98),
(1,4,75),
(1,5,90),
(1,6,30),
(1,7,40),
(2,1,70),
(2,2,60),
(2,3,70),
(2,4,105),
(2,5,95),
(2,6,30),
(2,7,10),
(3,1,70),
(3,2,60),
(3,3,70),
(3,4,75),
(3,5,99),
(3,6,30),
(3,7,10),
(4,1,70),
(4,2,60),
(4,3,70),
(4,4,89),
(4,5,99),
(4,6,30),
(4,7,19);
I had written Query myself to work out on this but cannot break it though.
select students.id, table_high.marks, table_high.faculty_name as high_faculty, table_high.subject_name as sub_high,
student_low.marks , student_low.faculty_name as faculty_low, student_low.subject_name as sub_low from students
inner join
(select students.id, students.student_name ,marks.marks, subject_joined.faculty_name, students_subject.subject_name from marks
inner join (select students_subject.id,students_subject.subject_name, faculty.faculty_name, students_subject.subject_faculty
from students_subject left join faculty on students_subject.subject_faculty = faculty.id)
as subject_joined on subject_joined.id = marks.subject_id
inner join faculty on subject_joined.subject_faculty = faculty.id
inner join students_subject on students_subject.id = marks.subject_id
inner join students on students.id = marks.student_id
order by 1, 3 desc) as table_high on table_high.id = students.id
inner join
(select students.id, students.student_name ,marks.marks, subject_joined.faculty_name, students_subject.subject_name from marks
inner join (select students_subject.id,students_subject.subject_name, faculty.faculty_name, students_subject.subject_faculty
from students_subject left join faculty on students_subject.subject_faculty = faculty.id)
as subject_joined on subject_joined.id = marks.subject_id
inner join faculty on subject_joined.subject_faculty = faculty.id
inner join students_subject on students_subject.id = marks.subject_id
inner join students on students.id = marks.student_id
order by 1, 3 ) as student_low on student_low.id = students.id
group by 1 ;
attaching screen of output :
Finally resolved this question!
The basic tweak that was required in summarizing this table is that the sub-table had to be joined with a combination of two columns as group by command just reflects the first row value in the summarized table for non-summarized cols, so reflecting values of max and min was not possible at the same time, to which I created sub tables filtering rows through double column joins and finally joined the table to the main student table.
The main table which was joined is Students.
Sub-Table 1 - hw (which summarized the data for highest)
sub-table 1.2 - high for highest marks tagging.
sub-table 2 - lw ( which summarized the table for the lowest)
sub-table 2.1 - low for minimum marks tagging.
Query >>
select students.id, students.student_name, lw.min_marks, lw.lower_subject, lw.lower_faculty,
hw.high_marks, hw.subject_name as high_subject, hw.faculty_name as higher_faculty
from students inner join
(select high.student_id, high.high_marks, high.subject_id, high.subject_name, high.faculty_name
from
(select marks.student_id, marks.marks as high_marks, sub_with_faculty.subject_id, sub_with_faculty.subject_name,
sub_with_faculty.faculty_name from marks
left join
(select students_subject.id as subject_id, students_subject.subject_name, faculty.faculty_name
from students_subject
left join faculty on students_subject.subject_faculty = faculty.id) as sub_with_faculty
on sub_with_faculty.subject_id = marks.subject_id) as high
inner join (select marks.student_id, max(marks) as marks from marks group by 1) as maximum on
maximum.student_id = high.student_id and maximum.marks = high.high_marks) as hw on
hw.student_id = students.id
inner join
(select low.student_id, low.low_marks as min_marks, low.subject_id as lower_subjectID, low.subject_name as lower_subject, low.faculty_name as lower_faculty
from
(select marks.student_id, marks.marks as low_marks, sub_with_faculty.subject_id, sub_with_faculty.subject_name,
sub_with_faculty.faculty_name from marks
left join
(select students_subject.id as subject_id, students_subject.subject_name, faculty.faculty_name
from students_subject
left join faculty on students_subject.subject_faculty = faculty.id) as sub_with_faculty
on sub_with_faculty.subject_id = marks.subject_id) as low
inner join (select marks.student_id, min(marks) as marks from marks group by 1) as minimum on
minimum.student_id = low.student_id and minimum.marks = low.low_marks) as lw on
lw.student_id = students.id;
This could be a good exercise for someone who's new to MySQL like me.

In a single query i want to get data from two different tables?

I want to get data from two tables in my DB. Here's what my tables might look like:
subject_code: semester, subjectcode, subjectname
markmanagment: subjectcode, semester, marks, rollno
i want to select rollno value=1 and semester value=1 and its corresponding values of marks and subject code from table markmanagment and for the same value of semester and subject code i want the subjectname from subject_code
This is a very basic join. Use an INNER JOIN to see only the results that have records in both tables - I used the subject code in the ON statement but you could also use semester here. Then specify your other conditions in the WHERE clause. In your SELECT statement, specify the columns you want to see returned by listing them in the following format: tablename.columnname. I use * here to return all columns.
SELECT *
FROM subject_code sc
INNER JOIN markmanagement mm ON sc.subjectcode = mm.subjectcode
WHERE sc.semester = mm.semester
AND sc.semester = 1
AND mm.rollno = 1
You should do a join table (Inner - Right - Left or Full, depending on your table).
Like :
SELECT * FROM subject_code
INNER JOIN markmanagment
ON subject_code.subjectcode = markmanagment.subjectcode;
WHERE markmanagment.rollno = 1 AND subject_code.semester = 1
You can specify multiple conditions in the on part of a join. For example:
select *
from markmanagement mm
join subject_code sc
on mm.subjectcode = sc.subjectcode
and mm.semester = sc.semester
where mm.semester = 1
and mm.rollno = 1
Hoping, i understood your problem correctly.
Tried to create same problem.
Sqlfiddle link - http://sqlfiddle.com/#!9/5a074b/2/0
create table subject_code
(
semester int,
subjectcode varchar(100),
subjectname varchar(100)
);
insert into subject_code values(1,'S01','subject1');
insert into subject_code values(1,'S02','subject2');
insert into subject_code values(2,'S01','subject1');
create table markmanagment
(
subjectcode varchar(100),
semester int,
marks int ,
rollno int
);
insert into markmanagment values('S01',1,75,1);
insert into markmanagment values('S02',1,80,1);
insert into markmanagment values('S01',2,85,1);
I think below query will help
select b.marks , b.subjectcode , a.subjectname
from subject_code a, markmanagment b
where a.semester = b.semester
and a.subjectcode = b.subjectcode
and a.semester = 1
and b.rollno = 1;

MySQL find results based on two different values of the same column

list employees names (Ename) who have both 49008 zip code customers and 49009 zip code customers.
I am struggling to answer the above query based on the above tables.
If the names match between tables assume constraint.
I can filter down to name and zip easily by left joins and group by but struggle after that, I don't know the proper where or having statement. I am assuming it could be done better by a sub query but not sure. Ideally, a single query.
Please and thank you.
1) Create and insert statements for example data:
Create table Employees (EM_Eno INT NOT NULL, EM_Ename VARCHAR(50), EM_Hire_Date DATE, PRIMARY KEY(EM_Eno));
Create table Customers (Customers_Cno INT NOT NULL, Customers_Cname VARCHAR(50), Customers_Street VARCHAR(50), Customers_Zip INT, Customers_Phone INT, primary key(Customers_Cno));
Create table Orders (Orders_Ono INT NOT NULL, Orders_Cno INT, Orders_Eno INT, Orders_Received DATE, Orders_Shipped DATE, primary key(Orders_Ono));
insert into Orders values
( 1,301,501,20161010,20161011);
( 2,302,501,20161011,20161012);
( 3,303,502,20161110,20161111);
( 4,304,502,20161110,20161112);
( 5,305,502,20161110,20161113);
( 6,306,503,20161112,20161114);
( 7,307,501,20161112,20161113);
( 8,308,503,20161112,20161115);
( 9,309,503,20161115,20161120);
(10,300,501,20161112,20161113);
insert into Customers values
(300,'Bryan','100 street',49009,1234567890),
(301,'Ryan','101 street',49008,1234567890),
(302,'Nick','102 street',49009,1234567890),
(303,'Nicholas','103 street',49009,1234567890),
(304,'Alexa','104 street',49009,1234567890),
(305,'Tori','105 street',49008,1234567890),
(306,'Scarlet','106 street',49008,1234567890),
(307,'Heather','100 street',49009,1234567890),
(308,'Amanda','107 street',49008,1234567890),
(309,'James','108 street',49008,1234567890);
insert into Employees values
(501,'Robert',20041010),
(502,'Sam',20050110),
(503,'Brandy',20050710);
2) Ideal end result is answering the query "list employees (names) who have both 49008-zipcode customers and 49009-zipcode customers."
3) Best Attempt thus far:
select Employees.EM_Ename
, Customers.Customers_Zip
from Employees
left
join Orders
on Employees.EM_Eno = Orders.Orders_Eno
left
join Customers
on Orders.Orders_Cno = Customers.Customers_Cno
group
by Employees.EM_Ename
, Customers.Customers_Zip;
The table names are altered slightly in the rextest, because other tables already exist there with the same name...
SELECT e.*
FROM tbl_employees e
JOIN tbl_orders o
ON o.orders_eno = e.em_eno
JOIN tbl_customers c
ON c.Customers_Cno = o.Orders_Cno
WHERE c.Customers_Zip IN(49008,49009)
GROUP
BY e.em_eno
HAVING COUNT(DISTINCT customers_zip) = 2;
http://rextester.com/HCNLU51847
This should be what you want:
select Min(e.Ename)
from #Employees e
join #Orders o on o.Eno = e.Eno
join #Customers c on c.Cno = o.Cno
join #Orders o2 on o2.Eno = e.Eno
join #Customers c2 on c2.Cno = o2.Cno
where o.Ono !=o2.Ono--c.Cno != c2.Cno and
and c.Zip = 49008 and c2.Zip = 49009
group by e.Ename,c.Zip
order by e.Ename
As you can see if you wanted more than those two the self joins become longer.

Using SQL to join tables with a junction table

I am trying to output data from 2 different tables in my database which are joined by a junction table.
Table 1: musician
Columns: musicianID, surname, fName
Table 2: musician_band
Columns: musicianID, bandID
Table 3: band
Columns: bandID, bandName, genre, yearFormed, origin
Just to start with I want to output:
fName, bandName
I tried using this query:
SELECT DISTINCT fName, bandName
FROM musician_band
JOIN musician ON musician.musicianID = musician_band.musicianID
JOIN band ON band.bandID = band.bandID
But instead of giving me a list of musicians and the bands that they're in, it is instead repeating the first name of the first musician in the databse and then a band name on the right, this is repeated for every band in the database, even where there shouldn't be a relationship between that particular musician and band. Like this:
fname bandName
musician1 band1
musician1 band2
musician1 band3
musician2 band1
musician2 band2
musician2 band3
etc...
How can I correct this so that it only outputs musicians in their correct bands?
There is a problem with your aliases. You need distinct alias for each referenced tables in your JOIN:
This sample query would work fine:
SELECT DISTINCT fName, bandName
FROM #musician_band mb
JOIN #musician m ON m.musicianID = mb.musicianID
JOIN #band b ON mb.bandID = b.bandID
Using this sample in SQL Server:
declare #musician table(musicianID int, surname varchar(50), fName varchar(50));
insert into #musician(musicianID, surname, fName
) values
( 1, 'Pete Doherty', 'Pete Doherty')
, ( 2, 'Damon Albarn', 'Damon Albarn')
declare #musician_band table(musicianID int, bandID int);
insert into #musician_band(musicianID, bandID) values
(1, 1)
, (1, 2)
, (2, 3)
, (2, 4)
declare #band table(bandID int, bandName varchar(50), genre varchar(50), yearFormed int, origin varchar(50));
insert into #band(bandID, bandName, genre, yearFormed, origin
) values
( 1, 'The Libertines', '', 0, '')
, (2, 'Babyshambles', '', 0, '')
, (3, 'Blur', '', 0, '')
, (4, 'Gorillaz', '', 0, '')
The output is:
fName bandName
Damon Albarn Blur
Damon Albarn Gorillaz
Pete Doherty Babyshambles
Pete Doherty The Libertines
Just replace #tablename by your own table. Is DISTINCT useful in this case?
You have not joined your tables correct
JOIN band ON band.bandID = band.bandID
Will get 1 row for EACH row in band.
Try this instead:
SELECT DISTINCT fName, bandName
FROM musician_band
JOIN musician ON musician.musicianID = musician_band.musicianID
JOIN band ON band.bandID = musician_band.bandID
You may even be able to remove the keyword DISTINCT with this solution
Assuming you want to include solo musicians:
select fName, bandName
from musician m
left join musician_band mb on mb.musicianID = m.musicianID
inner join band b on b.bandID = mb.bandID
order by fName, bandName
Even if you keep the inner join instead of left join, conceptually it's still ideal for the query to begin with the musician table, not musician_band. If you wanted musicians per band, the table order would be reversed. Either way there's no logic in starting with the junction table.
Multiple rows per musician is the expected result.
If you want this instead:
fName bandName
musician1 band1, band2, band3
musician2 band1, band2, band3
That requires a stored procedure or your app's code looping the query to build your desired data structure for output to the user.

MySQL returning 1 image for each product

This is very similar to Limit results from joined table to one row, but I'm struggling to get it to work exactly as I need...
The table structure is very similar:
CREATE TABLE tblProducts (
intProductID int(11) NULL AUTO_INCREMENT,
strProductName varchar(255) NULL,
PRIMARY KEY (intProductID)
);
CREATE TABLE tblProductImages (
intImageID int(11) NULL AUTO_INCREMENT,
intProductID int(11) NULL,
strImageName varchar(255) NULL,
intOrder int(11) NULL,
PRIMARY KEY (intImageID)
);
Populate the tables with:
INSERT INTO tblProducts (strProductName)
VALUES
('Product #1'), ('Product #2'), ('Product #3');
INSERT INTO tblProductImages (intProductID, strImageName, intOrder)
VALUES
(1, 'image_for_1.jpg', 1),
(2, '1st_image_for_2.jpg', 1),
(2, '2nd_image_for_2.jpg', 2);
I want to do a SELECT that returns all 3 products, with appropriate images. Similar to:
intProductID, strProductName, strImageName
1, Product #1, image_for_1.jpg
2, Product #2, 1st_image_for_2.jpg
3, Product #3, NULL
A simple solution would be:
SELECT intProductID, strProductName,
(SELECT strImageName
FROM tblProductImages
WHERE tblProductImages.intProductID = tblProducts.intProductID
ORDER BY intOrder
LIMIT 1)
FROM tblProducts
But this fails if I want to add & return another field from the tblProductImages (AltText for example)
I've setup a Fiddle at http://sqlfiddle.com/#!2/883c5/1
Thanks
This one will work, but is quite ugly
select p.intProductId, p.strProductName, pi.strImageName
from tblProducts p
inner join tblProductImages pi on pi.intProductID = p.intProductId
inner JOIN (select min(intOrder) minOrder, intProductID
FROM tblProductImages
GROUP BY intProductID) s
on s.intProductID = p.intProductID and s.minOrder = pi.intOrder
union
select p.intProductId, p.strProductName, pi.strImageName
from tblProducts p
left join tblProductImages pi on pi.intProductID = p.intProductId
where pi.intProductID is null;
alternative :
select p.intProductId, p.strProductName, pi.strImageName
from tblProducts p
left join tblProductImages pi on pi.intProductID = p.intProductId
where pi.intProductId is null or pi.IntOrder = (select min(intOrder)
from tblProductImages
where intProductId = pi.intProductId);
Sqlfiddle
You need to use aggregation (specifically GROUP BY). You want to GROUP BY the product name and use a LEFT JOIN so that the image does not need to exist.
SELECT strProductName, strImageName
FROM tblProducts LEFT JOIN tblProductImages USING (intProductID)
GROUP BY strProductName
Note that the image that is selected in this case is random (though usually it will be in intImageID order). This assumes that the actual image retrieved is not important as long as it is associated with the given product.
http://sqlfiddle.com/#!2/1391e6/1
For that exact output, you might need to modify your insert statement for table tblProductImages as:
INSERT INTO tblProductImages (intProductID, strImageName, intOrder) VALUES
(1, 'image_for_1.jpg', 1), (2, '1st_image_for_2.jpg', 1), (3, '2nd_image_for_3.jpg', 2);
And use the below Select statement:
SELECT tP.intProductID, tp.strProductName, tPI.strImageName
FROM tblProducts AS tP
INNER JOIN tblProductImages AS tPI ON tP.intProductID = tPI.intProductID;