This question already has answers here:
Sql query error: "From keyword missing" [closed]
(3 answers)
Closed 9 years ago.
The problem: Display hospitalid, hname, htype of hospital(s) which has the highest number of doctors associated with them.
The patient table:
patientid
pname
address
amount
ptype
The hospital table
hospitalid
hname
htype
The doctor table:
doctorid
dname
specialization
hospitalid
status
The billing table:
billingid
patientid
doctorid
fees
billdate
So far this is what I have:
select * from hospital where hospitalid =
(select hospitalid from doctor group by hospitalid having count ( doctorid ) =
(select max ( doctoramt ) from
(select count (doctorid) as doctoramt from doctor group by hospitalid) as tbltemp));
Try this but not tested
select * from hospital where hospitalid =
(select hospitalid from doctor group by hospitalid having count ( doctorid ) =
(select max ( doctoramt ) from
(select count (doctorid) as doctoramt from doctor group by hospitalid) as tbltemp)));
You can't use AS tbltemp to alias a table in Oracle. The AS keyword can only used to alias columns, not tables. You can either remove the AS keyword, or in this case since you don't refer to the alias, remove the whole AS tbltemp part. Here's an SQL Fiddle.
It looks like the parser initially tries to interpret AS as the alias name, and then doesn't know what the tbltemp is supposed to mean.
ZZa's approach is better anyway, but you could also use anayltic functions to avoid hitting the tables multiple times:
select hospitalid, hname, htype from (
select hospitalid, hname, htype, doc_count,
rank() over (order by doc_count desc) as rn
from (
select h.hospitalid, h.hname, h.htype,
count(d.doctorid) as doc_count
from hospital h
join doctor d on d.hospitalid = h.hospitalid
group by h.hospitalid, h.hname, h.htype
)
)
where rn = 1;
Another SQL Fiddle here. The innermost select does the counting, the next level ranks the grouped results in descending order of the number of doctors at each hospital, and the outermost restricts that to the highest-ranked.
Either way, if there's a tie - two hospitals with the same number of doctors - you'll get multiple rows back. If that's not what you want you'd need to decide how to break the tie.
Try this:
SELECT h.hospitalid, h.hname, h.htype
FROM hospital h, doctor d
WHERE h.hospitalid = d.hospitalid
GROUP BY h.hospitalid, h.hname, h.htype
HAVING COUNT(*) = (SELECT MAX(a) from (SELECT COUNT(*) a from doctor group by hospitalid));
Related
Given a table with two columns, department and employee, where every employee belongs to 1 department.
Given a list of employee ids, how do I select departments where all employees are in the list?
Department
Employee
finance
1
finance
2
marketing
3
marketing
4
IT
5
IT
6
given (2,3,4,5,6), returns ('marketing', 'IT')
(Note: DB flavor does not matter to me, you may use standard or DB-specific SQL)
Put your input id's into a table, join to the existing table, and check counts between input and the existing employees. If they match, output.
Change this to whatever RDBMS you're using
CREATE TEMP TABLE input_vals (id int);
insert into input_vals
values
(2),
(3),
(4),
(5),
(6);
with cte_joins AS (
select ot.department,
count(ot.employee) AS employee_count1,
sum(case when iv.id is not null then 1 else 0 end) AS employee_count2
from orig_table ot
left join input_vals iv
on ot.employee = iv.id
group by ot.department
)
select department
from cte_joins
where employee_count1 = employee_count2
You can use a couple of derived tables - one to get the count of employees in each department and one to get the count of employees in each department limited by your list of employees. Return the ones that match.
select
distinct full_list.department
from (
select
department,
count (*) as cnt
from
<your table>
group by department) full_list
inner join (
select
department,
count (*) as cnt
from
<your table>
group by department
where
employee in (2,3,4,5,6)
) limited_list
on full_list.department = limited_list.department
and full_list.cnt = limited_list.cnt
-- departments
select department from t where employee in (2,3,4,5,6);
-- departments containing extra persons
select department from t
where department in (select department from t where employee in (2,3,4,5,6))
and employee not in (2,3,4,5,6);
--- departments, employees from the list, with no extra
select department from t
where employee in (2,3,4,5,6) and department not in (
select department from t
where department in (
select department from t where employee in (2,3,4,5,6)
) and employee not in (2,3,4,5,6)
)
Assume you employee list is captured as a column in a table. Since you didn't provide any detail on the tool set you are using.
Something like this is a common pattern, depending on your dbms there's other more simple syntax.
select distinct
de.department
from department_employee de
where not exists
( select 1
from department_employee de2
where not exists
( select 1
from employee_list el
where de2.employee = el.employee
)
and de.department = de2.department
);
I have a table:
TABLE employee (
ID bigint,
name varchar,
department bigint
);
I would like to find a department that has minimal employees. (Count of rows in this table)
I believe this would require a HAVING statement with a nested sub-query, any help would be much appreciated.
I am using H2 database.
You could group by department and get the count of users in each department, order by the count and select top 1?
SELECT TOP 1
[department],
COUNT(*) AS [NoOfEmployees]
FROM [employee]
GROUP BY [department]
ORDER BY COUNT(*) ASC
There are tables Employees
CREATE TABLE Employees
(
id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
name nvarchar(100) NOT NULL,
depID int NOT NULL,
salary money NOT NULL,
FOREIGN KEY (depID) REFERENCES Departments(id)
);
and Payments
CREATE TABLE Payments
(
id int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
userID int NOT NULL,
createdDate date DEFAULT GETDATE(),
sum money NOT NULL,
FOREIGN KEY (userID) REFERENCES Employees(id)
);
I need to get names of Employees with the top three salaries for the last two years.
I tried to use the query below, but it doesn't work and I got an error.
SELECT TOP 3 name
FROM Employees
WHERE id in (SELECT id, SUM(sum) as SumTotal FROM Payments
WHERE (createdDate BETWEEN '2015-09-01' AND '2013-09-01')
ORDER BY SumTotal);
Error message:
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
How to make it work?
This is one way to do it using ctes.
Demo
with pymt as
(
SELECT userid, sum(sum) as sumtotal
FROM Payments
WHERE createdDate BETWEEN '2013-09-01' AND '2015-09-01'
group by userid
)
, ename as
(
select e.name, pymt.sumtotal, row_number() over(order by pymt.sumtotal desc) as rn
from pymt join employees e
on pymt.userid = e.id
)
select name
from ename
where rn < = 3;
People are way over-complicating things. I'm pretty sure this will get you what you want:
SELECT TOP 3 employees.id, Name,
Sum([sum]) AS [TotalPayments]
FROM Employees
inner join Payments on employees.id = payments.userid
WHERE createdDate BETWEEN '2013-09-01' and '2015-09-01'
Group By employees.id, Name
Order BY TotalPayments DESC
SqlFiddle to test it
If you want just the names column, you could wrap that query with another select:
select Name from (
SELECT TOP 3 employees.id, Name,
Sum([sum]) AS [TotalPayments]
FROM Employees
inner join Payments on employees.id = payments.userid
WHERE createdDate BETWEEN '2013-09-01' and '2015-09-01'
Group By employees.id, Name
Order BY TotalPayments DESC
) q
Try this:-
SELECT userID, SUM(sum) as SumTotal INTO #temp
FROM Payments
WHERE (createdDate BETWEEN '2015-09-01' AND '2013-09-01')
group by userID
ORDER BY SumTotal
SELECT TOP 3 name
FROM Employees e join #temp
on e.id = #temp.userID
select top 3
emp.id as id,
emp.name as name,
sum(pay.sum) as total
from employees emp
join payments pay
on emp.id = pay.user_id
--where pay.createdDate BETWEEN '2015-09-01' AND '2013-09-01'
where pay.createdDate BETWEEN '2013-09-01' AND '2015-09-01'
group by emp.id, emp.name
This should work
You have to do what the error message tell you to do, put the TOP where you have your order by :
SELECT name
FROM Employees
WHERE id in (SELECT TOP 3 id, SUM(sum) as SumTotal FROM Payments
WHERE (createdDate BETWEEN '2015-09-01' AND '2013-09-01')
ORDER BY SumTotal)
[EDIT]
If we follow error message, this should be ok:
SELECT name
FROM Employees
WHERE id in ( Select x.userId From (SELECT TOP 3 userId, SUM([sum]) as SumTotal FROM Payments
WHERE (createdDate BETWEEN '2015-09-01' AND '2013-09-01')
Group By userId
ORDER BY SumTotal) x);
SELECT TOP 3 Name,
Sum(sum) AS sum
FROM Employees
WHERE createdDate BETWEEN '2015-09-01' AND '2013-09-01'
Group By Name
Order BY 2 DESC
I have a Student table which contains following columns:
studentName, startYear, EndYear, classId, some more columns
This table contains startYear and EndYear for students of different class.
I want to write a query to find all the students name which took maximum years (diff b/w EndYear and startYear) to pass a class.
I want following three fields in select query
select studentName, classId, max(EndYear- startYear) as maxYears from Students group by classId;
but as group by doesn't contains studentname hence this query fails(and it make sense too).
Then I could do as :
Putting result of following query in temp table TEMP:
select classId, max(EndYear- startYear) from Students group by classId
and then join this temp table with student table.
select studentName, classId, EndYear- startYear from Student s join Temp t on s.classId = t.classId and (s.EndYear- s.startYear) = t.maxYears
But this doesn't look optimal to me. I am wondering what could be other ways to do it .
Try this query, which does a self-join to fetch the row with maximum (EndYear- startYear):
select s1.studentName, s1.classId, s1.EndYear-s1.startYear
from Student s1
inner join
(
select classId, max(EndYear- startYear)
from Students
group by classId
) s2
on s1.classId = s2.classId;
Try this:
SELECT *
FROM (
SELECT studentName, startYear, EndYear, classId,
DENSE_RANK() OVER(PARTITION BY classId ORDER BY endYear - startYear DESC) AS Rnk
FROM dbo.Student
) x
WHERE x.Rnk = 1;
The following script with correlated sub-query should be equivalent to the JOIN solution and even could be converted into JOIN by the SQL Server optimizer.
SELECT studentName, classId
FROM Students s
WHERE (EndYear- startYear) = (SELECT MAX(EndYear- startYear)
FROM Students sm
WHERE c.classId = sm.classId)
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
Display doctorid, dname, total fees received by the doctor(s) who have treated more than one patient?
Display hospitalid, hname, htype of hospital(s) which has the highest number of doctors associated with them.
Tables
table 1 - patient
patientid
pname
address
amount
ptype
table 2 - hospital
hospitalid
hname
htype
table 3 - doctor
doctorid
dname
specialization
hospitalid
status
table 4 - billing
billingid
patientid
doctorid
fees
billdate
So far this is what I have:
select
billing.doctorid,
sum (fees) as totalfees,
doctor.dname
from
billing, doctor
where
doctor.doctorid = billing.doctorid
group by
billing.doctorid,
doctor.dname
having
min ( billing.patientid ) <> max ( billing.patientid )
I'll help you with your first question, and I'll leave to you the second.
Display doctorid, dname, total fees received by the doctor(s) who have treated more than one patient?
Let's split this problem in pieces:
So you need first to know which doctors have treated more than one patient. That information is in the table billing. So:
select doctorId, count(patientId) as patientCount
from (select distinct doctorId, patientId from billing) as a
group by doctorId
having count(patientId)>1;
This query will return only the Ids of the doctors that have more than one patient. Notice that I'm using a subquery to deduplicate the doctor-patient tuple.
Now let's attack the other part of this question: The total fees of each doctor. Again, that info is in the table billing:
select doctorId, sum(fees) as totalFees
from billing
group by doctorId;
Finally, let's put it all together, and include the doctor's info, which is in the table doctor:
select
d.doctorId, d.doctorName, a.totalFees
from
doctor as d
inner join (
select doctorId, sum(fees) as totalFees
from billing
group by doctorId
) as a on d.doctorId = a.doctorId
inner join (
select doctorId, count(patientId) as patientCount
from (select distinct doctorId, patientId from billing) as a
group by doctorId
having count(patientId)>1;
) as b on d.doctorId = b.doctorId;
Hope this helps
Things you need to study and (or) keep in mind:
You need to understand how to relate data stored in different tables. Study how to use INNER JOIN (and also LEFT JOIN and RIGHT JOIN)
You need to understand how does GROUP BY works, and how to use aggregate functions (sum(), count(), etcetera).
You know how to write subqueries. Now try to use them not only for where conditions, but as data sources (including them in from statements)
Keep a copy of the reference manual of your RDBMS at hand. Also a good book on SQL can help you (go to a bookstore or library and find one you like).
Looks like you already got your answer, but since I wrote it up...
Select d.doctorID,
d.dName,
Sum(b.fees) [total fees received]
From doctor d
Join billing b
On d.doctorID = b.doctorID
Group By d.doctorID,
d.dName
Having Count(Distinct patientID) > 1
With CTE As
(
Select Rank() Over (Order By Count(d.doctorID) Desc) As priCount,
h.hospitalID,
h.hName,
h.hType,
Count(d.doctorID) As doctors
From hospital h
Join doctor d
On h.hospitalID = d.hospitalID
Group By h.hospitalID,
h.hName,
h.hType
)
Select hosptitalID,
hName,
hType
From CTE
Where priCount = 1