writing advanced sql query - mysql

I am working with this schema: http://classes.engr.oregonstate.edu/eecs/winter2013/cs275-400/tester/bsgSchema.html
and I am trying to achieve: Find the fname, lname and ship_instance id of all people who do not have Viper certificaton but are assigned to at least one instance of a Viper class ship (this includes all variants of Viper class ships). Return a row for every ship/person combination.)
I am close. I have written the following query:
SELECT DISTINCT p.fname, p.lname, si.id from bsg_people p
INNER JOIN bsg_ship_assignment sa ON sa.pid = p.id
INNER JOIN bsg_ship_class sc ON sc.id = sa.cid
INNER Join bsg_ship_instance si ON si.class = sa.cid
WHERE p.id NOT
IN (
SELECT cp.pid
FROM bsg_cert_people cp
INNER JOIN bsg_cert c ON c.id = cp.cid
WHERE cp.cid = '2'
)
AND sc.name = 'viper'
My query returns a number of extra instances.

SELECT p.fname
, p.lname
, sk.id
FROM bsg_people p
LEFT
JOIN bsg_cert_people cp
ON cp.pid = p.id
LEFT
JOIN bsg_cert c
ON c.id = cp.cid
AND c.title = 'viper'
JOIN bsg_ship_assignment ps
ON ps.pid = p.id
JOIN bsg_ship_instance sk
ON sk.id = ps.sid
AND sk.class = ps.cid
JOIN bsg_ship_class k
ON k.id = sk.class
WHERE k.name = 'viper'
AND c.id IS NULL;
+---------+----------+------+
| fname | lname | id |
+---------+----------+------+
| William | Adama | 8757 |
| Lee | Adama | 121 |
| Lee | Adama | 289 |
| Lee | Adama | 1104 |
| Laura | Roslin | 2343 |
| Gaius | Baltar | 289 |
| Samuel | Anders | 3203 |
| Samuel | Anders | 8757 |
| Brendan | Costanza | 7242 |
| Brendan | Costanza | 2343 |
+---------+----------+------+

SELECT bsg_people.fname, bsg_people.lname, bsg_ship_instance.id FROM bsg_people
INNER JOIN bsg_ship_assignment ON bsg_ship_assignment.pid = bsg_people.id
INNER JOIN bsg_ship_instance ON bsg_ship_assignment.sid = bsg_ship_instance.id
INNER JOIN bsg_ship_class ON bsg_ship_class.id = bsg_ship_instance.class
WHERE bsg_people.id NOT IN
(
SELECT bsg_cert_people.pid FROM bsg_cert_people
INNER JOIN bsg_cert ON bsg_cert_people.cid = bsg_cert.id
WHERE bsg_cert.title = 'Viper'
)
AND bsg_ship_class.name = 'viper'

Related

Special Join On 1 to N mapped columns

This question is an extension of AJC's Join On 1 to N mapped columns.
What if EMPLOYEE_DETAILS is like this:
+---------+-------------+--------------+
| EMP_ID | AREA_CODE | SECTOR_CODE |
+---------+-------------+--------------+
| 1223 | 5001 | 1001 |
| 3224 | (NULL) | 2001 |
| 3225 | 6001 | (NULL) |
+---------+-------------+--------------+
Then how would it be possible to map the respective names like this output:
+---------+-------------+--------------+
| EMP_ID | AREA_NAME | SECTOR_NAME |
+---------+-------------+--------------+
| 1223 | AREA 1 | SECTOR 1 |
| 3224 | (NULL) | SECTOR 2 |
| 3225 | AREA 2 | (NULL) |
+---------+-------------+--------------+
I thought about doing a union of joins like so,
select e.emp_id, a.LOCATION_NAME as area, s.LOCATION_NAME as sector
from EMPLOYEE_DETAILS e
join LOCATION_DETAILS a on a.id = e.AREA_CODE
join LOCATION_DETAILS s on s.id = e.SECTOR_CODE
union
select e.emp_id, a.LOCATION_NAME as area, NULL as sector
from EMPLOYEE_DETAILS e
join LOCATION_DETAILS a on a.id = e.AREA_CODE
union
select e.emp_id, NULL as area, s.LOCATION_NAME as sector
from EMPLOYEE_DETAILS e
join LOCATION_DETAILS s on s.id = e.SECTOR_CODE;
But then this would require me to use this as an internal query to get the expected result.
Is there a more concise way to go about doing this?
Why not left join twice?
select e.emp_id, a.location_name as area, s.location_name as sector
from employee_details e
left join location_details a on a.id = e.area_code
left join location_details s on s.id = e.sector_code
I can advice solution with single join like:
select
e.emp_id,
group_concat(if(ld.id = e.area_code, ld.location_name, null)) as area,
group_concat(if(ld.id = e.sector_code, ld.location_name, null)) as sector
from EMPLOYEE_DETAILS e
left join LOCATION_DETAILS ld
on (ld.id = e.area_code or ld.id = e.sector_code)
group by e.emp_id;
Here the fiddle: SQLize.online

Group Concat without conditionals

I'm trying to rewrite my query to use columns instead of rows. I have this query:
mysql> SELECT r.id as responseId, a.id as answerId, o.id, optionId, q.label as questionLabel, o.label as optionLabel
-> FROM answer a
-> INNER JOIN question q ON questionId = q.id
-> INNER JOIN answer_options_option ao ON a.id = ao.answerId
-> INNER JOIN `option` o ON ao.optionId = o.id
-> INNER JOIN response r ON a.responseId = r.id
-> ORDER BY r.id, a.id, o.id;
Which gives me this output:
+------------+----------+----+----------+---------------+-------------+
| responseId | answerId | id | optionId | questionLabel | optionLabel |
+------------+----------+----+----------+---------------+-------------+
| 1 | 1 | 2 | 2 | Q1 | no |
| 1 | 2 | 4 | 4 | Q2 | b |
| 2 | 3 | 1 | 1 | Q1 | yes |
| 2 | 4 | 3 | 3 | Q2 | a |
| 2 | 4 | 4 | 4 | Q2 | b |
| 2 | 4 | 5 | 5 | Q2 | c |
+------------+----------+----+----------+---------------+-------------+
But I would like to get this output:
+------------+-----------+-------+
| responseId | Q1 | Q2 |
+------------+-----------+-------+
| 1 | no | b |
| 2 | yes | a,b,c |
+------------+-----------+-------+
So I threw together this query:
mysql> SELECT r.id as responseId,
-> IF(q.label = 'Q1', GROUP_CONCAT(o.label), NULL) as Q1,
-> IF(q.label = 'Q2', GROUP_CONCAT(o.label), NULL) as Q2
-> FROM answer a
-> INNER JOIN question q ON questionId = q.id
-> INNER JOIN answer_options_option ao ON a.id = ao.answerId
-> INNER JOIN `option` o ON ao.optionId = o.id
-> INNER JOIN response r ON a.responseId = r.id
-> GROUP BY r.id;
But it gives me this output instead:
+------------+-----------+------+
| responseId | Q1 | Q2 |
+------------+-----------+------+
| 1 | no,b | NULL |
| 2 | yes,a,b,c | NULL |
+------------+-----------+------+
Is it even possible to use GROUP_CONCAT like this? Is there another way to accomplish what I'm trying to do?
Here's a Fiddle.
You need to move the GROUP_CONCAT outside the conditional so that you aggregate the options based on the question instead of just getting the entire aggregated result based on the question value:
SELECT r.id as responseId,
GROUP_CONCAT(CASE WHEN q.label = 'Q1' THEN o.label END ORDER BY o.label) as Q1,
GROUP_CONCAT(CASE WHEN q.label = 'Q2' THEN o.label END ORDER BY o.label) as Q2
FROM answer a
INNER JOIN question q ON questionId = q.id
INNER JOIN answer_options_option ao ON a.id = ao.answerId
INNER JOIN `option` o ON ao.optionId = o.id
INNER JOIN response r ON a.responseId = r.id
GROUP BY r.id;
Output:
responseId Q1 Q2
1 no b
2 yes a,b,c
Demo on dbfiddle

SQL select average grade for students

I'm learning sql and I'm having a bit of trouble with this,
I need the average grade of every student on each subject.
I made the following tables.
students
|id_student|name|
subjects
|id_subject|name|
grades
|id_grade|value|
And im linking them using these tables:
students_subjects
|id_student|id_subject|
subjects_grades
|id_subject|id_grade|
students_grades
|id_student|id_grade|
Any help appreciated
I'm trying
SELECT students.name, subjects.name, grades.value
FROM students
INNER JOIN students_subjects
ON students.id_student = students_subjects.id_student
INNER JOIN subjects
ON subjects.id_subject = students_subjects.id_subject
INNER JOIN students_grades
ON students_grades.id_student = students.id_student
INNER JOIN grades
ON students_grades.id_grade = grades.id_grade
INNER JOIN subjects_grades
ON grades.id_grade = subjects_grades.id_grade
And i get the following table
| name | name | value |
|----------|---------|-------|
| Nico | class1 | 70 |
| Nico | class1 | 40 |
| Nico | class2 | 70 |
| Nico | class2 | 40 |
| Fadia | class1 | 60 |
| Fadia | class1 | 55 |
| Cristian | class2 | 50 |
| Cristian | class2 | 40 |
But if i do AVG(grades.value) I only get the first row
Just add a group by:
SELECT students.name, subjects.name, AVG(grades.value)
FROM students
INNER JOIN students_subjects
ON students.id_student = students_subjects.id_student
INNER JOIN subjects
ON subjects.id_subject = students_subjects.id_subject
INNER JOIN students_grades
ON students_grades.id_student = students.id_student
INNER JOIN grades
ON students_grades.id_grade = grades.id_grade
INNER JOIN subjects_grades
ON grades.id_grade = subjects_grades.id_grade
GROUP BY students.name, subjects.name

MySQL join and sum issue / combine queries

I have created a points example to demonstrate my issue.
A members can reward other members points, I am trying to build a query which will display the points a user has and points user has given.
Database Example
Members,
+----+----------+
| id | username |
+----+----------+
| 1 | user1 |
| 2 | user2 |
| 3 | user3 |
+----+----------+
Points,
+-------+--------+--------+
| IDFor | IDFrom | Pointz |
+-------+--------+--------+
| 1 | 2 | 5 |
| 1 | 2 | 5 |
| 3 | 1 | 2 |
+-------+--------+--------+
The return I am looking for is,
+-----------+--------+-------+
| username | Pointz | Given |
+-----------+--------+-------+
| user1 | 10 | 2 |
| user2 | 0 | 10 |
| user3 | 2 | 0 |
+-----------+--------+-------+
Both my queries return,
+-----------+--------+-------+
| username | Pointz | Given |
+-----------+--------+-------+
| user1 | 10 | 4 |
| user2 | 0 | 10 |
| user3 | 2 | 0 |
+-----------+--------+-------+
http://sqlfiddle.com/#!2/32ae6/6
SELECT a.`username`, sum(a.`Pointz`), sum(b.`Pointz`) FROM
(SELECT * FROM `members`
LEFT JOIN `Example`.`Points` AS p ON `members`.`id` = p.`IDFor` ) AS a
LEFT JOIN
(SELECT * FROM `members`
LEFT JOIN `Example`.`Points` AS n ON `members`.`id` = n.`IDFrom` ) AS b
ON a.id = b.id
GROUP BY a.`id`
http://sqlfiddle.com/#!2/32ae6/7
SELECT `members`.`username`,sum(p.`Pointz`),sum(n.`Pointz`)
FROM `members`
LEFT JOIN `Example`.`Points` as p ON p.`IDFor` = `members`.`id`
LEFT JOIN `Example`.`Points` as n ON n.`IDFrom` = `members`.`id`
GROUP BY `members`.`id`
Seems to be a common question that pops up but have not found a solution, all my other attempts from similar questions have not ended well, Thanks all.
This is what u want
select a.username, ifnull(Pointz, 0) Pointz, ifnull(Given, 0) Given from
(SELECT id, `username`, sum(`Pointz`) Pointz FROM `members`
LEFT JOIN `Points` ON `members`.`id` = `Points`.`IDFor` group by id) a
left join
(SELECT id, `username`, sum(`Pointz`) Given FROM `members`
LEFT JOIN `Points` ON `members`.`id` = `Points`.`IDFrom` group by id) b
on a.id = b.id
Try:
SELECT m.username,
COALESCE( pr.preceived, 0 ) as Pointz,
COALESCE( pg.pgiven, 0 ) as Given
FROM members m
LEFT JOIN ( SELECT IDFor, sum(pointz) as preceived
FROM Points
GROUP BY IDFor ) pr
ON m.id = pr.IDFor
LEFT JOIN ( SELECT IDFrom, sum(pointz) as pgiven
FROM Points
GROUP BY IDFrom ) pg
ON m.id = pg.IDFrom
http://sqlfiddle.com/#!2/32ae6/48
Try this:
SELECT m.`username`,
ifnull((SELECT sum(`Pointz`) FROM Points p
WHERE p.IDFor = m.id ), 0),
ifnull((SELECT sum(`Pointz`) FROM Points p
WHERE p.IDFrom = m.id ), 0)
FROM Members m
SQLFiddle

How to write a MySQL Join query

I've two tables.
users:
uid | city | username | flag |
10 | New York | john | 1 |
14 | Tokyo | kawasaki | 1 |
15 | Tokyo | coder | 1 |
groupmember:
id | uid | groupid |
1 | 10 | 16 |
2 | 14 | 16 |
3 | 15 | 21 |
The 'uid' in both the tables are the same.
I want to select all users who are in city "tokyo" who are also in the group with groupid "16" .
So the query resutl should be (in this case)
14 | Tokyo | kawasaki | 1 |
SELECT u.uid, u.city, u.username, u.flag
FROM users u
JOIN groupmember g ON u.uid = g.uid
WHERE u.city = 'Tokyo'
AND g.groupid = 16;
select u.* from users u join groupmember gm on u.uid = gm.uid
where u.city='Tokyo' and gm.groupid=16
SELECT * FROM
users INNER JOIN groupmember
ON users.uid = groupmember.uid
AND groupmember.groupid = 16
AND users.city = 'Tokyo'
SELECT u.uid, u.city, u.username, u.flag
FROM users u, groupmember g
WHERE u.uid = g.uid
AND u.city = 'Tokyo'
AND g.groupid = 16;