Information from two rows from a query into 1 row - sql

I have 3 database tables:
Workoutput which defines the work done by employees.
employeeinfo which defines relationships for each person. The three important fields are child, parent and relation. A person can be listed
a couple of times with different relationships i.e,
child parent relation
12 3 2
12 43 4
With the relation referring to whether the parent is mentor or manager
And finally a standard employee table which has comprehensive listings about the employee id, name, login etc.
I would like to have a results table which defines all the people above that one worker on a daily basis, but have it defined in one row
on a daily basis. At the moment I can only define them on separate days with the following query.
SELECT workoutput.Day, workoutput.Employee, employee.name
FROM workoutput INNER JOIN
employeeInfo ON workoutput.ID = [employeeinfo].son INNER JOIN
[employee] ON [employeeInfo].parent = [employee].id INNER JOIN
[employee] AS [employee1] ON workoutput.[Employee ID] = [employee1].id
This returns the relationships, but there will be two rows for each day, as the majority of people will have two people above them (Mentor and Manager).
29/03/2010 00:00:00 Employee1 Manager 3
29/03/2010 00:00:00 Employee1 Mentor 1
and I would like
29/03/2010 00:00:00 Employee1 Mentor 1 Manager3
I also have another table at my disposal, which define the relationships it has two rows, id and type of relation, with id referring to a relation id defined in the employeeinfo table.
Is that possible?
Thanks

Is there a maximum number of parents for a child (i.e. relations)? And is there one per relation?
If so, you can do this with a PIVOT to get from your current output to your desired output.
However, if there are a fixed number of roles, you could also include that in your joins from the outset:
SELECT wo.Day, wo.Employee, e.name, er1.name, er2.name, etc.
FROM workoutput AS wo
INNER JOIN [employee] AS e
ON workoutput.[Employee ID] = e.id
LEFT JOIN employeeInfo AS ei1
ON e.ID = ei1.child
AND ei1.relation = 1
LEFT JOIN [employee] AS er1
ON ei1.parent = er1.id
LEFT JOIN employeeInfo AS ei2
ON e.ID = ei2.child
AND ei2.relation = 2
LEFT JOIN [employee] AS er2
ON ei2.parent = er2.id
etc.

Not with standard SQL. The usual approach here would be to sort the table and then use it as input into a reporting tool or a custom program.
In SQL Server, it would be possible to write a stored procedure which did this. It depends on how badly you want it.

you can group on employee identifier and group_concat the parents.
http://explainextended.com/2010/06/21/group_concat-in-sql-server/

if your relation field is constant (i.e. only a set number of relationship types) you can write a CASE statement to transpose it like this...
SELECT workoutput.Day, workoutput.Employee,
CASE relation WHEN 2 THEN employee.name END AS relation2, CASE relation WHEN 4 THEN employee.name END AS relation4
FROM workoutput
INNER JOIN employeeInfo ON workoutput.ID = [employeeinfo].son
INNER JOIN [employee] ON [employeeInfo].parent = [employee].id
INNER JOIN [employee] AS [employee1] ON workoutput.[Employee ID] = [employee1].id
GROUP BY workoutput.Day, workoutput.Employee, employee.name
However, a rather glaring downside to this is that if you add new relations then you will start to get duplicate rows so this should only be used if you have rigorous version controlling in your SP's or similar
EDIT
Apologies, I missed off the GROUP BY clause :)

Related

How to link tables correctly in SQL to add roles to staff?

Currently I have a staff table with columns:
Staff_Id, first_name, Surname.
My second table is:
Id, management_role.
When I link the tables each staff member gets added to every management role. So for example a person in first table called Jim is added three times as manager, supervisor, intern and this happens for every staff.
Some things to consider that are your ID columns are primary keys for their respective tables. If not are every value in the column is unique? Also are ids not
From your description you might be using a cross join here. The thing you need is inner join so it joins the matching id's together.
So you can do
SELECT *
FROM staff_table as st
INNER JOIN management_table as mt
ON st.Staff_Id = mt.ID

SQL for many to one to many table

I have three tables in an Access database that I am using in java via ucanaccess.
Patients (PK Pt_ID)
Endoscopy (PK Endo_ID, FK Pt_ID)
Histology (PK Histol_ID, FK Pt_ID)
1 patient can have many endoscopies
1 patient can have many histologies
Endoscopy and histology are not related
I want to retrieve all the Endoscopies and histologies for a single patients in a single SQL query. Although I can write select statements for two tables I don't know how to do this across the three tables. Is it something like this
Select *.Endoscopy,*.Histology from Patients INNER JOIN Endoscopy, Histology ON Patient.Pt_Id=Endoscopy.Pt_ID, Patient.Pt_Id=Histology.Pt_ID
I'm sure that's a mess though...
What kind of SQL DB are you using?
I believe this works on most.
SELECT * FROM Patients, Endoscopy, Histology
WHERE Patient.Pt_Id=Endoscopy.Pt_ID
AND Patient.Pt_Id=Histology.Pt_ID
Also, I belive you have these switched around *.Endoscopy,*.Histology If you need to use that it should be Endoscopy.*, Histology.*
You can use the following query to select both endoscopies and histologies :
SELECT p.Pt_ID
, e.Endo_ID
, h.Histol_ID
FROM Patients p
INNER JOIN Endoscopy e ON p.Pt_Id = e.Pt_ID
INNER JOIN Histology h ON p.Pt_Id = h.Pt_ID
But I'm not sure this is really what you want. You might need to map the tables Patients, Endoscopy and Histology into Java classes ? In this case, you can consider the Java Persistence API (JPA). It helps you to handle these tables in your java code. Here is a JPA Quick Guide.
First idea is to use inner join (with correct syntax) but this is wrong. inner join returns patients who have both procedure. Pure left join returns additionally patients who have none. So this is the solution:
SELECT Patients.Pt_PK, Endoscopy.*, Histology.*
FROM Patients
LEFT JOIN Endoscopy ON Patients.Pt_Id = Endoscopy.Pt_ID
LEFT JOIN Histology ON Patients.Pt_Id = Histology.Pt_ID
--exclude patients who don't have any
where coalesce(Endoscopy.Endo_ID, Histology.Histol_ID) is not null
If you have multiple Endoscopy records or multiple Histology records for the same Patient then you will receive duplicate/repeated records in your SELECT. I do no think there is a way around that unless you use 2 SELECT statements instead of 1.
SELECT Endoscopy.*, Histology.*
FROM Patients
INNER JOIN Endoscopy ON Patients.Pt_Id = Endoscopy.Pt_ID
INNER JOIN Histology ON Patients.Pt_Id = Histology.Pt_ID
To select all records on a table in the select its table name/table alias .*
INNER JOIN will only select records where there is a relationship, once one of these tables does not contain a Pt_ID where it is contained in any one of the other tables then no record will be displayed with that Pt_ID
To add additional tables continue to add additional join statements
You used Patients (with S) in one location and Patient (no S) in another, make sure you use the correct naming. I am guessing its Patients but maybe its Patient.
This statement does almost the same as the above but uses LEFT JOIN syntax so that you will always get records for both tables even if one of the two tables does not have a record for a patient.
SELECT Endoscopy.*, Histology.*
FROM Patients
LEFT JOIN Endoscopy ON Patients.Pt_Id = Endoscopy.Pt_ID
LEFT JOIN Histology ON Patients.Pt_Id = Histology.Pt_ID
WHERE Histology.Histol_ID IS NOT NULL OR Endoscopy.Endo_ID IS NOT NULL
The added WHERE clause ensures that you do not get a record with all NULL values where there is a patient but no records in either of those tables.

Ms-Access: counting from 2 tables

I have two tables in a Database
and
I need to retrieve the number of staff per manager in the following format
I've been trying to adapt an answer to another question
SELECT bankNo AS "Bank Number",
COUNT (*) AS "Total Branches"
FROM BankBranch
GROUP BY bankNo
As
SELECT COUNT (*) AS StaffCount ,
Employee.Name AS Name
FROM Employee, Stafflink
GROUP BY Name
As I look at the Group BY I'm thinking I should be grouping by The ManID in the Stafflink Table.
My output with this query looks like this
So it is counting correctly but as you can see it's far off the output I need to get.
Any advice would be appreciated.
You need to join the Employee and Stafflink tables. It appears that your FROM clause should look like this:
FROM Employee INNER JOIN StaffLink ON Employee.ID = StaffLink.ManID
You have to join the Eployee table twice to get the summary of employees under manager
select count(*) as StaffCount,Manager.Name
from Employee join Stafflink on employee.Id = StaffLink.EmpId
join Employee as Manager on StaffLink.ManId = Manager.Id
Group by Manager.Name
The answers that advise you on how to join are correct, assuming that you want to learn how to use SQL in MS Access. But there is a way to accomplish the same thing using the ACCESS GUI for designing queries, and this involves a shorter learning curve than learning SQL.
The key to using the GUI when more than one table is involved is to realize that you have to define the relationships between tables in the relationship manager. Once you do that, designing the query you are after is a piece of cake, just point and click.
The tricky thing in your case is that there are two relationships between the two tables. One relationship links EmpId to ID and the other links ManId to ID.
If, however, you want to learn SQL, then this shortcut will be a digression.
If you don't specify a join between the tables, a so called Cartesian product will be built, i.e., each record from one table will be paired with every record from the other table. If you have 7 records in one table and 10 in the other you will get 70 pairs (i.e. rows) before grouping. This explains why you are getting a count of 7 per manager name.
Besides joining the tables, I would suggest you to group on the manager id instead of the manager name. The manager id is known to be unique per manager, but not the name. This then requires you to either group on the name in addition, because the name is in the select list or to apply an aggregate function on the name. Each additional grouping slows down the query; therefore I prefer the aggregate function.
SELECT
COUNT(*) AS StaffCount,
FIRST(Manager.Name) AS ManagerName
FROM
Stafflink
INNER JOIN Employee AS Manager
ON StaffLink.ManId = Manager.Id
GROUP BY
StaffLink.ManId
I don't know if it makes a performance difference, but I prefer to group on StaffLink.ManId than on Employee.Id, since StaffLink is the main table here and Employee is just used as lookup table in this query.

PL/SQL -- Join Child Tables With Unrelated Rows

I'm trying to perform a join in PL/SQL to get a list of parent
records with multiple child tables back as part of a single query.
I'm not an Oracle expert. If I wrote a query like this:
SELECT PEOPLE.PersonName, JOBS.JobName, CREDENTIALS.CredentialName
FROM PEOPLE
LEFT OUTER JOIN JOBS
ON PEOPLE.PersonName = JOBS.PersonName
LEFT OUTER JOIN CREDENTIALS
ON PEOPLE.PersonName = CREDENTIALS.PersonName
WHERE PEOPLE.PersonName = 'James'
I would get a table back that lists every combination of
job and credential like so:
RN PERSON JOB CREDENTIAL
1 James Developer MBA
2 James Developer PhD
3 James Developer MCAD
4 James QA MBA
5 James QA PhD
6 James QA MCAD
That's fine, and exactly how you expect a left outer
join to work. But what I need is for the JOB and
CREDENTIAL columns to only list each element once and
be unrelated to each other -- but still list James'
child records on, and only on, rows where PERSON is
James.
RN PERSON JOB CREDENTIAL
1 James Developer MBA
2 James QA PhD
3 James (null) MCAD
But I'm not sure how to write this join. (The
idea is that C# code will take the query results
and convert it into one parent PERSON object with
lists of references to two child JOB objects and
three child CREDENTIAL objects.
Outside this example, though, there's actually
a lot more than two child tables, so I can't
filter a result-set which is the product of all
the child-combinations; I need the columns to
be unrelated lists.
I'm willing to deal with hairy SQL to achieve
this, but it needs to be one query and not
multiple queries with the results combined on
the C# side.
Thanks to anyone who can help!
You need to enumerate each list and then use that for joining:
select p.PersonName, j.JobName, c.CredentialName
from Person p left outer join
(select j.*,
row_number() over (partition by PersonName order by jobname) as seqnum
from jobs
) j
on p.PersonName = j.PersonName full outer join
(select c.*,
row_number() over (partition by PersonName order by CredentialName) as seqnum
from Credentials
) c
on p.PersonName = c.PersonName and
j.seqnum = c.seqnum
WHERE p.PersonName = 'James'
This query is doing more work, because it assigns seqnum for all persons. You can put the WHERE clause in the subqueries to make it more efficient, if you are really only selecting one person at a time.

Why table exists two times under different aliases in FROM clause of SQL query?

I have this query:
SELECT to_number(gr.code) group_index,
gr.NAME group_name, f.*,
gr.description gr_desc
FROM obj$groups gr, obj$group_objects gro,
obj$group_objects gro2, tdf$flex_fields f,
inv$requests w, inv$Direction_Criterions c
WHERE gr.NO = gro.object_no
AND gro.group_no = obj$group_service.path_to_no(
'Some Condition String',
tdf$flex_field_service.get_local_list_group_no)
AND gro2.group_no = gr.NO
AND f.NO = gro2.object_no
AND w.no = 11593597
AND c.direction_no = w.direction_no
AND f.no = c.criterion_no
ORDER BY to_number(gr.code), f.name
Why are two same tables (group_objects) present here? I tried to reverse-engineer this, but couldn't myself, maybe anyone here already know of this trick?
This happens in Oracle database.
It's an operation called self-join. When you want to join records from the same table.
It usually happens when you have records related to records in the same table. Example:
create table tree
(
id number primary key,
parent_id number,
value varchar2(100)
);
So, if you want to retrieve nodes and their parents you would do:
select c.id, c.value, p.value as parent_value
from tree c inner join tree p on (c.parent_id = p.id)
Something similar is happening in the query you posted.
group_objects is being joined to groups in the clause gro2.group_no = gr.NO and to flex_fields in the clause f.NO = gro2.object_no. I suspect this covers the case where obe set of group object isn't exactly the same as the other set and this limits the rows in the two joins where one join removes a group_object that would then not be available to join to the other table.
It's hard to divine the original programmer's intent from this snippet, especially without a description of what the various tables hold.
However, it appears to me as if the final result of this query is supposed to report information from two different records from the group_objects table — one in which the group no matches "Some Condition String" and the other in which the group no matches a column value from the groups table. If I had to guess, it's retrieving an operation in which an item was transferred between two groups, or substituted to be used in place of an object from a second group, or something like that.
For illustration, the equivalent with the standard EMP table would be:
select e.ename, m.ename manager_name
from emp e, emp m
where m.empno = e.mgr;
Or in more modern syntax:
select e.ename, m.ename manager_name
from emp e
join emp m on m.empno = e.mgr;
i.e. show the names of employees with managers and the name of their managers.
The point is that the same table is used twice in the query in a different "role". It need not be a self-join, there could be another table (or more) in between like this:
select e.ename, pm.ename projmanager_name
from emp e
join project_assignments pa on p.empno = pa.empno
join projects p on p.proj_id = pa.proj_id
join emp pm on pm.empno = p.projmanager_empno;
i.e. show the names of employees assigned to projects and show the name of the project manager of that project.