Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I been unable to figure out a way to do the query below without using a parameter. The database has over 2000 possible values for the parameter, so what I want is to output a table that lists all the parameters in one column, and the output from the query in the other. Below is a simplified version of what works now:
SELECT [enter name] AS ExclName,
Sum(revenue) AS MaxRev
FROM tbltasks
WHERE tbltasks.task IN (SELECT DISTINCT tblabilities.task
FROM tblabilities
WHERE tblabilities.name <> [enter name]);
tbltasks tblpeople tblabilities
task revenue name name task
A 10 Bob Bob A
B 9 Tom Tom A
C 8 Jack Jack A
D 7 Mary Tom B
E 6 Diane Jack B
F 5 Alice Mary B
G 4 Sam Jack C
H 3 Mary C
I 2 Diane C
Mary D
Diane D
Alice D
Diane E
Alice E
Sam E
Bob F
Tom G
Alice H
Right now I run the query, manually enter a name like "Bob" when prompted, and get a single row of output like:
ExclName MaxRev
Bob 47
But what I want is the complete table:
ExclName MaxRev
Bob 47
Tom 48
Jack 52
Mary 52
Diane 52
Alice 49
Sam 52
Or in other words, I want to know what maximum revenue could be achieved by a team that excluded the named member. The real application is more complicated, as there are other groupings involved (eg. if exclude Jack, must also exclude Mary), but I think if I solve the simple problem above I can handle the rest.
I keep thinking there must be a way to use the names from tblPeople and link them to the MaxRev calculation with a single query, but I haven't been able to figure it out. I could do some code to loop through all the parameters, but am hoping there is a more straightforward solution. Appreciate anyone's input.
you can simply extend your query for all people:
select
p.name as ExclName,
sum(t.revenue) as MaxRev
from tblpeople as p
cross join tbltasks as t
where
t.task in
(
select l.task
from tblabilities as l
where l.name <> p.name
)
group by p.name
sql fiddle demo
update. Still don't know which RDBMS do you using, here's SQL Server solution, it's possible to split solution below into temp table, variable and query to improve performance, but it should be faster than first one anyway (if you have appropriate indexes on your table):
with cte as (
select l.[name], sum(t.[revenue]) as [revenue]
from tblabilities as l
inner join tbltasks as t on t.[task] = l.[task]
where
not exists
(
select *
from tblabilities as tl
where tl.[task] = l.[task] and tl.[name] <> l.[name]
)
group by l.[name]
)
select
p.[name] as ExclName,
t.revenue_total - isnull(c.[revenue], 0) as MaxRev
from tblpeople as p
left outer join cte as c on c.[name] = p.[name]
outer apply (
select sum(tt.[revenue]) as revenue_total
from tbltasks as tt
where
tt.[task] in (select tl.[task] from tblabilities as tl)
) as t
order by p.[name]
sql fiddle demo
Related
I'm trying to write a Ado SQL statement for my Access table and I'm getting the wrong results.
Employee Table
ID Name DriverID
1 Alex 1
2 Tom 2
3 Trevor 3
4 PHIL 0
5 Gina 4
Vehicle Table
ID PLATE EMPLOYEEID INSERVICE
1 123XYZ 1 N
2 456GFR 2 Y
3 TFV4FG 3 Y
4 F6GK7D 4 Y
5 GEY7GH 1 Y
I want result of All employes and to display the Vehcicle info if they are assigned to it.
Result should be
Name Plate
Alex GEY7GH
Tom 456GFR
Trevor TFV4FG
PHIL
Gina F6GK7D
SELECT Employee.ID, Employee.FirstName, Vehicles.Plate, Vehicles.InService
FROM Employee LEFT JOIN Vehicles ON Employee.ID = Vehicles.DriverID
WHERE (((Vehicles.InService)=True));
Does not display PHIL who is not assigned to a vehicle.
Just add the condition inside the join, making sure to use parentheses to avoid problems when joining with constants or anything but simple equals:
SELECT Employee.ID, Employee.FirstName, Vehicles.Plate, Vehicles.InService
FROM Employee LEFT JOIN Vehicles ON (Employee.ID = Vehicles.DriverID AND Vehicles.InService = True)
From the above tables, it looks like the DriverID Column in employee table aligns with the EmployeeID column in the vehicles table and the issue is the on clause in the join.
SELECT
Employee.ID
,Employee.FirstName
,Vehicles.Plate
,Vehicles.InService
FROM
Employee
LEFT JOIN Vehicles ON Employee.DRIVERID = Vehicles.EMPLOYEEID
WHERE
(((Vehicles.InService)=True));
Well, in a normal database, you would move the WHERE condition to the ON clause. But I don't think that MS Access supports this:
SELECT e.ID, e.FirstName, v.Plate, v.InService
FROM Employee as e LEFT JOIN
Vehicles as v
ON e.ID = v.DriverID AND v.InService = True;
An alternative is a subquery:
SELECT e.ID, e.FirstName, v.Plate, v.InService
FROM Employee as e LEFT JOIN
(SELECT v.*
FROM Vehicles as v
WHERE v.InService = True
) as v
ON e.ID = v.DriverID ;
names:
id, first, last
879 Scotty Anderson
549 Melvin Anderson
554 Freddy Appleton
321 Grace Appleton
112 Milton Appleton
189 Jackson Black
99 Elizabeth Black
298 Jordan Frey
parents:
id, student_id
549 879
321 554
112 554
99 189
298 189
Expected Output
(without the 'Student:' / 'Parent:')
Student: Anderson, Scotty
Parent: Anderson, Melvin
Student: Appleton, Freddy
Parent: Appleton, Grace
Parent: Appleton, Milton
Student: Black, Jackson
Parent: Black, Elizabeth
Parent: Frey, Jordan
Using the data above, how can I achieve the expected output?
I currently use SQL similar to this to get a list of current students and names.
select b.last, b.first
from term a, names b
where a.id = b.id(+)
order by b.last
Which returns:
Anderson, Scotty
Appleton, Freddy
Black, Jackson
My question is how to take the parents table and add to this query so it has this output:
Anderson, Scotty
Anderson, Melvin
Appleton, Freddy
Appleton, Grace
Appleton, Milton
Black, Jackson
Black, Elizabeth
Frey, Jordan
The idea in a query like this is to break the data down into something that helps you solve the problem, and then put it back together as needed. In this case I'm going to make use of common table expressions, which allows me to treat queries as tables and then recombine them handily.
Looking at the desired results it looks like we want to have the students appear first, followed by their mothers (ladies first :-), and then their fathers. So, OK, let's figure out how to extract the needed data. We can get the students and their associated data pretty simply:
select distinct p.student_id as student_id,
n.first,
n.last,
0 as type
from parents p
inner join names n
on n.id = p.student_id
The type column, with its constant value of zero, is just used to identify that this is a student. (You'll see why in a minute).
Now, getting the mother's is a bit more difficult because we don't have any gender information to use. However, we'll use what we have, which is names. We know that names like Melvin, Milton, and Jordan are "guy" names. (Yes, I know Jordan can be a girls name too. My daughter has a male coach named Jordan, and a female teammate named Jordan. Just go with it - for purposes of argument in this case Jordan is a guys name, 'K? 'K :-). So we'll use that information to help us identify the mom's:
select p.student_id, n.first, n.last, 1 as type
from parents p
inner join names n
on n.id = p.id
where first not in ('Melvin', 'Milton', 'Jordan')
Notice here that we assign the value of 1 to the type column for mothers.
Similarly, we'll find the dads:
select p.student_id, n.first, n.last, 2 as type
from parents p
inner join names n
on n.id = p.id
where first in ('Melvin', 'Milton', 'Jordan')
And here we assign a value of 2 for the type.
OK - given the above we just need to combine the data properly. We don't want to use a JOIN, however, because we want the names to get spit out one after the other from the query - and the way we do THAT in SQL is with the UNION or UNION ALL operator. (Generally, you're going to want to use UNION ALL, because UNION will check the result set to ensure there are no duplicates - which in the case of a large result set takes, oh, more or less FOREVER!). And so, the final query looks like:
with all_students as (select distinct p.student_id as student_id,
n.first,
n.last,
0 as type
from parents p
inner join names n
on n.id = p.student_id),
all_mothers as (select p.student_id, n.first, n.last, 1 as type
from parents p
inner join names n
on n.id = p.id
where first not in ('Melvin', 'Milton', 'Jordan')),
all_fathers as (select p.student_id, n.first, n.last, 2 as type
from parents p
inner join names n
on n.id = p.id
where first in ('Melvin', 'Milton', 'Jordan'))
select last || ', ' || first as name from
(select * from all_students
union all
select * from all_mothers
union all
select * from all_fathers)
order by student_id desc, type;
We just take the student data, followed by the mom data, followed by the dad data, then sort it by the student ID from highest to lowest (I just looked at the desired results to figure out that this should be a descending sort), and then by the type (which results in the student (type=0) being first, following by their mother (type=1) and then their father (type=2)).
SQLFiddle here
Share and enjoy.
generic SQL, mmmmm I'd like there to be A generic SQL :)
First off you want to stop using the antique (+) join syntax that is exclusive to Oracle
select b.last, b.first
from term a
LEFT OUTER JOIN names b ON a.id = b.id
order by b.last
That is way more generic! (nb: You can abbreviate to just LEFT JOIN)
Now to concatenate (Last Name comma space First Name) there are options some not generic
SQL Server/MySQL and others supporting CONCAT()
select CONCAT(b.last , ', ', b.first)
from term a
LEFT OUTER JOIN names b ON a.id = b.id
order by b.last
not all versions of Oracle or SQL Server support CONCAT()
Oracle's concat() only takes 2 parameters; grrrrr
ORACLE
select b.last || ', ' || b.first
from term a
LEFT OUTER JOIN names b ON a.id = b.id
order by b.last
In this form Oracle generally handles data type conversions automatically (I think, please check on date/timestamps maybe others)
TSQL (Sybase, MS SQL Server)
select b.last + ', ' + b.first
from term a
LEFT OUTER JOIN names b ON a.id = b.id
order by b.last
In this form you must explicitly cast/convert data types to n|var|char for concatenation if not already a string type
For your list of concatenated names:
You need in addition to the last name a method to retain the family group together, plus distinguish between student and parent. As you want just one column of names this indicates you need a column of id's that point to the last and first names. So making some assumptions about the table TERM my guess is you list the students from that, then append the parents that relate to that group of students, and finally to output the required list in the required order.
select
case when type = 1 then 'Student' else 'Parent' end as who
, names.last || ', ' || names.first as Name
from (
select
STUDENT_ID as name_id
, STUDENT_ID as family_id
, 1 as TYPE
from term
union all
select
PARENTS.ID as name_id
, PARENTS.STUDENT_ID as family_id
, 2 as TYPE
from PARENTS
inner join term on PARENTS.STUDENT_ID = term.STUDENT_ID
) sq
inner join NAMES ON sq.name_id = NAMES.ID
order by
names.last
, sq.family_id
, sq.type
see: http://sqlfiddle.com/#!4/01804/6
This is too long for a comment.
Your question doesn't make sense. The easy answer to the question is:
select last, first
from names;
But it seems unlikely that is what you want.
Your sample query mentions a table term. That is not mentioned elsewhere in the question. Please clarify the question or delete this one and ask another.
I think I see what you're trying to do. I think you could set up a derived table and then query it. Set up something like: case when student id= id then 1 else 0 as match or whatever. Then query your derived table and group by match.
I would do it like that in SQL:
Select last +', '+ first as fullname from names;
I'm working through some problems and I can't seem to get the expected results for this one. The question is below with what is in my code right now and also the expected results. If anyone help that would be great. I'm just trying to get a understanding on this and can't seem to get my head around what exactly this is asking as you can see my code I have now isn't close to what the expected result is as of right now. Also I added the schema this will show whats in what table if needed for your guys help.
Question:
List the course number of courses wherein students have received grades for every one of the possible defined grade types. Order by course number.
My code so far:
SELECT g.Student_id, g.Grade_type_code
FROM Grade g LEFT OUTER JOIN Section s
ON g.Section_id = s.Section_id
GROUP BY g.Student_id, g.Grade_type_code
ORDER BY g.Student_id;
Any help would be great, also here is the Schema.
DBMS: I'm using Oracle SQL Developer
Here is the Expected Result
COURSE_NO
----------
20
25
100
120
122
125
130
135
Note: The Chapter for this problem is based off using
LEFT OUTER JOIN
My Current results
STUDENT_ID GRADE_TYPE_CODE
---------- ---------------
102 FI
102 HM
102 MT
102 PA
102 QZ
103 FI
103 HM
103 MT
103 PA
103 QZ
104 FI
104 HM
Based on your ER diagram I believe this query should return a list of courses whose enrolled students have collectively received all of the grade types listed in the GRADE_TYPE table.
select s.course_no,
c.descr,
count(distinct g.grade_type_code) as num_grade_types
from grade g
join enrollment e
on g.student_id = e.student_id
and g.section_id = e.section_id
join section s
on e.section_id = s.section_id
join course c
on s.course_no = c.course_no
group by s.course_no, c.descr
having count(distinct g.grade_type_code) = (select count(grade_type_code)
from grade_type)
I didn't notice your expected result was only the course # (you can just get rid of the columns you don't want from the select list). Also the join to the COURSE table is only there to get the course description, so if you don't want the course description selected, you do not need that join.
You need to select COURSE_NO instead. And also use JOIN and not LEFT JOUTER JOIN.
Something like this:
select COURSE_NO from
(
SELECT distinct (s.COURSE_NO)
FROM Grade g JOIN Section s
ON g.Section_id = s.Section_id
)
ORDER BY s.COURSE_NO;
I have two tables - 'Users' and 'Supervision'
For this example, my users table is very simple:-
Users
=====
ID (PK)
UserName
Some users manage other users, so I've built a second table 'Supervision' to manage this:-
Supervision
===========
UserID
SuperID - this is the ID of the staff member that the user supervises.
This table is used to join the Users table to itself to identify a particular users supervisor. It might be that a user has more than one supervisor, so this table works perfectly to this end.
Here's my sample data in 'Users':-
userID userName
1 Bob
2 Margaret
3 Amy
4 Emma
5 Carol
6 Albert
7 Robert
8 Richard
9 Harry
10 Arthur
And my data in 'Supervision':-
userID superID
1 2
1 3
2 4
2 5
3 4
3 5
6 1
6 7
7 8
7 9
9 10
If I want to see who directly reports to Bob, writing an SQL query is straightforward, and tells me that Margaret and Amy are his direct reports.
What I want to do however is to write a query that shows everybody who comes under Bob, so it would need to look at Bobs direct reports, and then their direct reports, and so on - it would give Margaret, Amy, Emma and Carol as the result in this case.
I'm assuming this requires some kind of recursion but I'm completely stuck..
You should use recursive CTE:
WITH RCTE AS
(
SELECT * FROM dbo.Supervision WHERE UserID = 1
UNION ALL
SELECT s.* FROM dbo.Supervision s
INNER JOIN RCTE r ON s.userID = r.superID
)
SELECT DISTINCT u.userID, u.userName
FROM RCTE r
LEFT JOIN dbo.Users u ON r.superID = u.userID
SQLFiddle DEMO
Sounds to me like you need a Recursive CTE. This article serves as a primer, and includes a fairly similar example to the one you have:
http://blog.sqlauthority.com/2012/04/24/sql-server-introduction-to-hierarchical-query-using-a-recursive-cte-a-primer/
Hope it helps.
WITH MyCTE
AS (
-- ID's and Names
SELECT SuperID, ID
FROM Users
join dbo.Supervision
on ID = dbo.Supervision.UserID
WHERE UserID = 1
UNION ALL
--Who Manages who...
SELECT s.SuperID, ID
FROM Supervision s
INNER JOIN MyCTE ON s.UserID = MyCTE.SuperID
WHERE s.UserID IS NOT NULL
)
SELECT distinct MyCTE.ID, NAMES.UserName, '<------Reports to ' as Hierarchy, res_name.UserName
FROM MyCTE
join dbo.Users NAMES on
MyCTE.ID = NAMES.ID
join dbo.Users res_name
on res_name.ID = MyCTE.SuperID
order by MyCTE.ID, NAMES.UserName, res_name.UserName
I have four tables in my Database:
Person that contains ID(PK) and Name.
Person_Skill that contains ID(PK), PID(FK), Skill(FK) and SkillLevel(FK).
Skill that contains ID(PK) and SkillLabel.
SkillLevel that contains ID(PK) and Name.
Every skill has level from 0 to 7
Now I want to display all the skill that the person has(Include the skilllevel = 0)
Select
[dbo].Person.Name as Name,
[Skill].SkillLabel as SkillName,
[Person_Skill].[SkillLevel] as SkillLevel
From
([dbo].Person inner join [dbo].[Person_Skill] ON [dbo].[Person_Skill].PID= Person.ID)
inner join [dbo].[Skill] ON [dbo].[Person_Skill].Skill=Skill.ID
The above code only display the skill that person has from level 1 to level 7.
I believe the reason I only get the skill from level 1 to 7 is because the person table only contains the skill from level 1 to 7, but I'm not sure about this. I got the database from other. If my assumption is correct, is there anyway to do this? To get all the skills in the skill table and display the skill level that the person has(Include skillllevel =0).
Sample Data:
Person
ID Name
----------
1 Michael
2 Alex
Person_Skill
ID PID SkillID Skill_Level
5 1 10 5
6 2 11 1
7 1 12 7
8 1 13 5
Skill
ID Name
10 java
11 C++
12 HTML
13 ASP
14 C
15 .NET
16 C#
17 Objective
The expect results are;
Name SkillName SkillLevel
----------------------------
Alex java 0
Alex C++ 1
Alex HTML 0
Alex ASP 0
Alex C 0
Alex .NET 0
Alex C# 0
Alex Objective C 0
Michael java 5
Michael C++ 0
Michael HTML 7
Michael ASP 0
Michael C 0
Michael .NET 5
Michael C# 0
Michael Objective C0
The current query only output
Alex C++ 1
Michael java 5
Michael HTML 7
Michael .NET 5
Edit, if you want to return all skill names for each person, then you will want to use:
select d.p_name,
d.s_name SkillName,
coalesce(ps.Skill_Level, 0) Skill_Level
from
(
select p.id p_id, p.name p_name, s.id s_id, s.name s_name
from person p
cross join skill s
) d
left join person_skill ps
on d.p_id = ps.pid
and d.s_id = ps.skillid
left join skill s
on ps.skillid = s.id
See SQL Fiddle with Demo
If you want to include all Skills 0-7, then you will want to use a LEFT JOIN. You query will be similar to the following:
select p.Name as Name,
s.SkillLabel as SkillName,
ps.[SkillLevel] as SkillLevel
from [dbo].[Skill] s
left join [dbo].[Person_Skill] ps
on ps.Skill=s.ID
left join [dbo].Person p
on ps.PID = p.ID
Edit, without seeing any data it is difficult to determine. But if you want to retrieve all SkillLevels, then you will need to include that table. You might need to use:
select
p.Name as Name,
s.SkillLabel as SkillName,
sl.[Name] as SkillLevel
from SkillLevel sl
left join [dbo].[Person_Skill] ps
on ps.SkillLevel=sl.ID
left join [dbo].[Skill] s
on ps.Skill = s.Id
left join [dbo].Person p
on ps.PID = p.ID
You would want to use a LEFT JOIN which when tableA is inner joined on tableb would return all records from tableA regardless of whether or not there was a match from tableB
Therefore, if there are no persons with a skill of 0, you will still get back all of the person records.
An INNER JOIN will only return rows where there is a match on both sides. So in your code if a person does not have a skill with level 0 it would not be returned.
You could do a LEFT or RIGHT join and these get all the rows from the table on either the left or the right side. I think you probably want to use a left join, but without knowing more about your schema it's hard to say for sure. See the answer given to the question Left join and Left outer join in SQL Server for more detail on the differences in different join types