How can I join multiple rows in just one single row through mysql?
Example :
Student Table
Sno.| Name | Subjects
1. | ABC | 1
2. | ABC | 3
3. | ABC | 4
4. | FMC | 2
5. | ABC | 4
6. | JBC | 4
Papers Table:
Sno. | Paper Name | Type
1. French Optional
2. English Mandatory
3. Japenese Optional
4. Maths Optional
Now I want it in this format
Sno.| Name| Sub1 | Sub2 | Sub3 | Sub4 |
1. | ABC | French | Japenese| Maths | Null |
2. | FMC | Null | Null | Null | Null |
3. | JBC | Maths | Null | Null | Null |
What i want to select is papers name from papers table and student name, four subjects from the other table. I only want to see those paper which are optional. I am not sure what to do?
I think the concept you are looking for is pivoting.
Version with join
select
name,
max(if(s.subject = 1 and p.type = 'Optional', p.paper_name, null)) as subject1,
max(if(s.subject = 2 and p.type = 'Optional', p.paper_name, null)) as subject2,
max(if(s.subject = 3 and p.type = 'Optional', p.paper_name, null)) as subject3,
max(if(s.subject = 4 and p.type = 'Optional', p.paper_name, null)) as subject4
from
students s
inner join
papers p on p.sno = s.subject
group by s.name
SQL Fiddle Demo
Version with subselects instead of a join
select
name,
max(
case
when subject = 1
then (select paper_name from papers p where p.sno = subject and type = 'Optional')
else
null
end) as subject1,
max(
case
when subject = 2
then (select paper_name from papers p where p.sno = subject and type = 'Optional')
else
null
end) as subject2,
max(
case
when subject = 3
then (select paper_name from papers p where p.sno = subject and type = 'Optional')
else
null
end) as subject3,
max(
case
when subject = 4
then (select paper_name from papers p where p.sno = subject and type = 'Optional')
else
null
end) as subject4
from
students
group by
name
SQL Fiddle Demo
Related
I need some help building a query and joining the results in one row, here are the tables:
tbl_contacts
id | name | lastname | email
1 | daniel1| lastname1 | daniel1#test.com
2 | daniel2| lastname2 | daniel2#test.com
tbl_contacts_var
cid | name | value|
1 | VAR1 | d1 |
1 | VAR2 | d2 |
1 | VAR3 | d3 |
I have tried the following query which is the closest desired result I could get, however I need it different, here is what I have tried:
SELECT tbl_contacts.*,
( SELECT GROUP_CONCAT(value SEPARATOR ',')
FROM tbl_contacts_var WHERE
tbl_contacts.id = tbl_contacts_var.cid )
as attributes
FROM tbl_contacts WHERE tbl_contacts.id = 1
And this is the output
id | name | lastname | email |attributes
1 | daniel1| lastname1 | daniel1#test.com|d1,d2,d3
However, the output I am looking for is more like this:
id | name | lastname | email | VAR1 | VAR2 | VAR3 |
1 | daniel1| lastname1 | daniel1#test.com| d1 | d2 | d3 |
I tried with JOIN but it returns three rows, and I need the full result in one row, also I need to use the value of "name" in the second table as a column name for the result, is this possible?
You can try the query below
SELECT c.name, c.name, c.lastname, c.email
, MAX(
CASE cv.name
WHEN 'VAR1' THEN cv.VALUE
ELSE NULL
END
) AS VAR1
, MAX(
CASE cv.name
WHEN 'VAR2' THEN cv.VALUE
ELSE NULL
END
) AS VAR2
, MAX(
CASE cv.name
WHEN 'VAR3' THEN cv.VALUE
ELSE NULL
END
) AS VAR3
FROM tbl_contacts c
JOIN tbl_contacts_var cv ON cv.cid = c.id
GROUP BY c.name, c.name, c.lastname, c.email
I am trying to allow for the custom creation of tables and columns using a few tables in the database.
The Data is in a table called CustomData,
ID | CustomEntries_ID | CustomColumn_ID | Value
-------------------------------------------------
1 | 1 | 1 | Pheven
2 | 1 | 2 | Routine
3 | 1 | 3 | Planned
4 | 1 | 4 | 2014
5 | 2 | 1 | John
6 | 2 | 2 | Routine
7 | 2 | 3 | Planned
8 | 2 | 4 | 2017
SELECT * FROM (
(SELECT CustomEntries_ID AS ID, `Value` AS `Name` FROM `CustomData` WHERE CustomColumn_ID = 1) AS T1 NATURAL JOIN
(SELECT CustomEntries_ID AS ID, `Value` AS `Servive` FROM `CustomData` WHERE CustomColumn_ID = 2) AS T2 NATURAL JOIN
(SELECT CustomEntries_ID AS ID, `Value` AS `Type` FROM `CustomData` WHERE CustomColumn_ID = 3) AS T3 NATURAL JOIN
(SELECT CustomEntries_ID AS ID, `Value` AS `Model` FROM `CustomData` WHERE CustomColumn_ID = 4) AS T4 NATURAL JOIN
)
The first problem seems to be if there is an entry missing the CustomData table all data relating to that customEntries_ID are excluded.
The second issue is this seems very inefficient, I'm sure someone has a better suggestion for this query or to allow for Custom table creation without actually creating new tables on the database.
Just use conditional aggregation:
SELECT customernetyid
max(case when CustomColumn_ID = 1 then value end) as name,
max(case when CustomColumn_ID = 2 then value end) as service,
max(case when CustomColumn_ID = 3 then value end) as type,
max(case when CustomColumn_ID = 4 then value end) as model
FROM CustomData
GROUP BY customentryid;
I have two tables:
Employees
id | fullName | birth | speciality
1 | A A A | 01/01/1980 | Manager
2 | B B B | 01/01/1980 | Developer
3 | C C C | 01/01/1980 | User
EmployeesStatus
ID | status | dateChange
1 | 1 | 01/01/2010
2 | 1 | 01/01/2013
3 | 1 | 01/01/2015
3 | 2 | 01/01/2016
and I want to seletect the following data
ID | Full name | Bith date | speciality | Date hired | Date fired
Result has to be:
ID | Full name | Bith date | speciality | Date hired | Date fired
1 | A A A | 01/01/1980 | Manager | 01/01/2010 | null
2 | B B B | 01/01/1980 | Developer | 01/01/2013 | null
3 | C C C | 01/01/1980 | User | 01/01/2015 | 01/01/2016
3 | C C C |01/01/1980 | User | 01/01/2017 | null
my code:
SELECT Employees.id , Employees.fullName, Employees.birth, Employees.speciality,
(SELECT dateChange FROM EmployeesStatus WHERE status=1 AND id=Employees.id) datehired,
(SELECT dateChange FROM EmployeesStatus WHERE status=2 AND id=Employees.id) datefired FROM Employees
hase as result the following message:
Msg 512, Level 16, State 1, Line 1 Subquery returned more than 1
value. This is not permitted when the subquery follows =, !=, <, <= ,
, >= or when the subquery is used as an expression.
Any thoughts?
You should use a join instead of an = based on subquery
SELECT
Employees.id
, Employees.fullName
, Employees.birth
, Employees.speciality
, e1.dateChange as datehired, e2.dateChange as datefired
FROM Employees
INNER JOIN EmployeesStatus es1 on e1.status=1 AND e1.id=Employees.id
LEFT JOIN EmployeesStatus es2 on e2.status=2 AND e2.id=Employees.id
Or you could use in clause instead of = on subquery
In addition to #scaisEdge answer:
SELECT
Employees.id,
Employees.fullName,
Employees.birth,
Employees.speciality
e1.dateChange as datehired,
MIN(e2.dateChange) as datefired
FROM Employees
INNER JOIN EmployeesStatus es1 on e1.status=1 AND e1.id=Employees.id
LEFT JOIN EmployeesStatus es2 on e2.status=2 AND e2.id=Employees.id
AND e2.dateChange > e1.dateChange
GROUP BY Employees.id,
Employees.fullName,
Employees.birth,
Employees.speciality,
e1.dateChange
Try following:
SELECT E.ID,E.FULLNAME,E.BIRTH,E.SPECIALITY,ED.DATE_HIRED,ED.DATE_FIRED
FROM EMPLOYEES E,
(SELECT ID,
MAX(CASE WHEN STATUS=1 THEN DATECHANGE ELSE NULL END)DATE_HIRED,
MAX(CASE WHEN STATUS=2 THEN DATECHANGE ELSE NULL END)DATE_FIRED
FROM EMPLOYEESSTATUS
GROUP BY ID)ED
WHERE E.ID=ED.ID
This query will be faster in term of performance and will give you same result.
try this
SELECT e.id ,
e.fullName,
e.birth,
e.speciality,
CASE WHEN t1.status = 1 then max(t1.dateChange) else null end as "Date Hired",
CASE WHEN t2.status = 2 then max(t2.dateChange) else null end as "Date Fired",
FROM Employees e
LEFT JOIN EmployeesStatus t1 on t1.id = e.id and t1.status = 1
LEFT JOIN EmployeesStatus t2 on t2.id = e.id and t1.status = 2
Hope this works..
I have 400+ students who have a range of 1-4 for scores across their courses. Students can take anywhere from 3-6 courses. How can I read through the data and pull back only those students who got a score of '4' on all their grades. I'm thinking some type of ranking function, but it's not coming to me.
I have this code which builds a temp table and gives me a list of each student (along with other data) with their score for each grade. Now I need to go through one row at a time and identify those students who only have 4's for all courses.
SELECT *
INTO #TempTableHonors
FROM
(
SELECT id.lastName + ', ' + id.firstName + COALESCE(' ' + LEFT(id.middleName,1),'') AS 'student'
, p.stateID
, cust.value AS RTAdvisor
, en.grade
, cs.name as ClassName
, gs1.score
, gt1.name
FROM Enrollment en
JOIN Person p on en.personID = p.personID
JOIN [Identity] id on p.currentIdentityID = id.identityID and p.personID = id.personID
JOIN Trial tr ON tr.calendarID = en.calendarID AND tr.active = 1
JOIN Roster rs ON rs.personID = en.personID AND rs.trialID = tr.trialID
JOIN Section sc ON sc.sectionID = rs.sectionID AND sc.trialID = tr.trialID
JOIN Course cs ON cs.courseID = sc.courseID AND cs.calendarID = en.calendarID
JOIN ScheduleStructure ss ON ss.calendarID = en.calendarID
JOIN TermSchedule ts ON ts.structureID = ss.structureID
JOIN Term tm ON tm.termScheduleID = ts.termScheduleID AND tm.seq = 1
JOIN (GradingScore gs1 JOIN GradingTask gt1 ON gt1.taskID = gs1.taskID)
ON gs1.calendarID = en.calendarID AND gs1.personID = en.personID AND gs1.sectionID = sc.sectionID AND gs1.termID = tm.termID
LEFT JOIN customstudent cust on cust.personID = p.personID and cust.attributeID = 321
WHERE 1=1
AND en.calendarID = 1054
AND en.serviceType = 'P'
-- AND en.endDate is null
AND (gt1.name = 'Quarter - Habits of Work')
group by id.lastName + ', ' + id.firstName + COALESCE(' ' + LEFT(id.middleName,1),'')
, p.stateID
, cust.value
, en.grade
, cs.name
, gs1.score
, gt1.name
) AS x
One approach:
SELECT student
FROM ( ... query that returns students and score ... )
GROUP
BY student
HAVING MIN(score) = 4
;
So, for example, if the ... query that returns students and score ... part returns something like this:
student | score
---------+-------
Jim | 4
Jim | 3
Kara | 4
Kara | 4
then the overall query will return this:
student
---------
Kara
There's a few ways to go about this. One method:
Imagine a table
+---------+--------+-------+
| student | course | grade |
+---------+--------+-------+
| 1 | a | 4 |
| 1 | b | 4 |
| 1 | c | 3 |
| 1 | d | 4 |
| 2 | a | 4 |
| 2 | b | 4 |
| 2 | d | 4 |
| 3 | a | 4 |
| 3 | b | 4 |
| 3 | c | 4 |
| 3 | d | 3 |
+---------+--------+-------+
Grab all of the students that have records that are not a 4:
SELECT student FROM grades WHERE grade <> 4 GROUP BY student;
Then get a list of students that aren't in that list
SELECT student FROM grades WHERE student NOT IN (SELECT student FROM grades WHERE grade <> 4 GROUP BY student);
If I have a table of cases:
CASE_NUMBER | CASE_ID | STATUS | SUBJECT |
----------------------------------------------------------------
3108 | 123456 | Closed_Billable | Something Interesting
3109 | 325124 | Closed_Billable | Broken printer
3110 | 432432 | Open_Assigned | Email not working
And a table of calls:
PARENT_ID | STATUS | DUR(H) | DUR(M) | SUBJECT
---------------------------------------------------------------
123456 | Held | 1 | 30 | Initial discussion
123456 | Cancelled | 0 | 0 | Walk user through
123456 | Held | 0 | 45 | Remote debug session
325124 | Held | 1 | 0 | Consultation
325124 | Held | 1 | 15 | Needs assessment
432432 | Held | 1 | 30 | Support call
And a table of meetings:
PARENT_ID | STATUS | DUR(H) | DUR(M) | SUBJECT
-------------------------------------------------------
123456 | Held | 3 | 15 | On-site work
325124 | Held | 2 | 0 | Un-jam printer
432432 | Held | 1 | 0 | Reconnect network
How do I do a select with these parameters (this is not working code, obviously):
SELECT cases.case_number, cases.subject, calls.subject, meetings.subject
WHERE cases.status="Closed_Billable" AND (calls.status="Held" OR meetings.status="Held)
LEFT JOIN cases
ON cases.case_id = calls.parent_id
LEFT JOIN cases
ON cases.case_id = meetings.parent_id
and end up with a "faked" nested table like:
CASE_NUMBER | CASE SUBJECT | # CALLS | # MEETINGS | CALL SUBJECT | MEETING SUBJECT | DURATION (H) | DURATION (M) | TOTAL
-----------------------------------------------------------------------------------------------------------------------------------------
3108 | Something Interesting | 2 | 1 | | | | | 5.5H
| | | | Initial Discussion | | 1 | 30 |
| | | | Remote Debug Session | | 0 | 45 |
| | | | | On-site work | 3 | 15 |
3109 | Broken printer | 2 | 1 | | | | | 4.25H
| | | | Consultation | | 1 | 0 |
| | | | Needs assessment | | 1 | 15 |
| | | | | Un-jam printer | 2 | 0 |
I've tried joins and subqueries the best I can figure out, but I get repeated entries - for example, each Meeting in a Case will show say 3 times, once for each Call in that case.
I'm stumped! Obviously there's other fields I'm pulling here, and doing COUNTs of Calls and Meetings, and SUMs of their durations, but I'd be happy just to show a table/sub-table like this.
Is it even possible?
Thanks,
David.
Assembling a query result in the exact format you want is .. somewhat of a pain. It can be done, but presentation stuff like that is best left to the application.
That said, this will do what you want:
select case when case_id > floor(case_id) then ''
else case_number
end case_number,
coalesce(q1.c, '') calls,
coalesce(q2.c, '') meetings,
coalesce(calls.subject, '') `call subject`,
coalesce(meetings.subject, '') `meeting subject`,
case when calls.subject is not null then calls.dhour
when meetings.subject is not null then meetings.dhour
else ''
end dhour,
case when calls.subject is not null then calls.dmin
when meetings.subject is not null then meetings.dmin
else ''
end dhour,
coalesce(q3.total, '') total
from
(
select case_number, case_id
from cases where status = 'Closed_Billable'
union select case_number, concat(case_id, '.1')
from cases where status = 'Closed_Billable'
union select case_number, concat(case_id, '.2')
from cases where status = 'Closed_Billable'
) main
left join
(select parent_id, count(*) c
from calls
where status != 'Cancelled'
group by parent_id ) q1
on q1.parent_id = case_id
left join
(select parent_id, count(*) c
from meetings
group by parent_id) q2
on q2.parent_id = case_id
left join
(select parent_id, sum(dhour + m) total
from
(select parent_id, dhour, dmin / 60 m
from calls
where status != 'Cancelled'
union all
select parent_id, dhour, dmin / 60 m
from meetings
) qq
group by parent_id
) q3
on q3.parent_id = case_id
left join calls
on concat(calls.parent_id, '.1') = main.case_id
left join meetings
on concat(meetings.parent_id, '.2') = main.case_id
order by case_id asc
Note, i've renamed your duration fields because i dislike the parenthesis in them.
We have to mangle the case_id a little bit inside the query in order to be able to get you your blank rows / fields - those are what makes the query cumbersome
There's a demo here: http://sqlfiddle.com/#!9/d59d4/21
edited code to work with different schema in comment fiddle
select case when case_id > floor(case_id) then ''
else case_number
end case_number,
coalesce(q1.c, '') calls,
coalesce(q2.c, '') meetings,
coalesce(calls.name, '') `call subject`,
coalesce(meetings.name, '') `meeting subject`,
case when calls.name is not null then calls.duration_hours
when meetings.name is not null then meetings.duration_hours
else ''
end duration_hours,
case when calls.name is not null then calls.duration_minutes
when meetings.name is not null then meetings.duration_minutes
else ''
end duration_hours,
coalesce(q3.total, '') total
from
(
select case_number, id as case_id
from cases where status = 'Closed_Billable'
union select case_number, concat(id, '.1') as case_id
from cases where status = 'Closed_Billable'
union select case_number, concat(id, '.2') as case_id
from cases where status = 'Closed_Billable'
) main
left join
(select parent_id, count(*) c
from calls
where status != 'Cancelled'
group by parent_id ) q1
on q1.parent_id = case_id
left join
(select parent_id, count(*) c
from meetings
group by parent_id) q2
on q2.parent_id = case_id
left join
(select parent_id, sum(duration_hours + m) total
from
(select parent_id, duration_hours, duration_minutes / 60 m
from calls
where status != 'Cancelled'
union all
select parent_id, duration_hours, duration_minutes / 60 m
from meetings
) qq
group by parent_id
) q3
on q3.parent_id = case_id
left join calls
on concat(calls.parent_id, '.1') = main.case_id
left join meetings
on concat(meetings.parent_id, '.2') = main.case_id
order by case_id asc
You can't really get final results like that without some seriously ugly "wrapper" queries, of this sort:
SET #prevCaseNum := 'blahdyblahnowaythisshouldmatchanything';
SET #prevCaseSub := 'seeabovetonotmatchanything';
SELECT IF(#prevCaseNum = CASE_NUMBER, '', CASE_NUMBER) AS CASE_NUMBER
, IF(#prevCaseNum = CASE_NUMBER AND #prevCaseSubject = CASE_SUBJECT, '', CASE_SUBJECT) AS CASE_SUBJECT
, etc.....
, #prevCaseNum := CASE_NUMBER AS prevCaseNum
, #prevCaseSubject = CASE_SUBJECT AS prevCaseSub
, etc....
FROM ( [the real query] ORDER BY CASE_NUMBER, etc....) AS trq
;
And then wrap all that with another select to strip the prevCase fields.
And even this still won't give you the blanks you want on the "upper right".