Use Common Table Expression to get difference between two sets of data - sql

I'm trying to use a common table expression to find the differences between two queries I wrote. The first query returns how many patients belong to each ROOMID(each ID represent a specific room).
Second query I have is how many patients that belong to each ROOMId have surgery operated on them. PatientID represent each patient.
select roomID, count(distinct patientID) as totalinsurgery
from data with (nolock)
where ptprocess = 'surgery'
group by clientid, batchid
Second query:
select CAroomid, sum(patientsinroom) as patientsinroom
from data
group by caroomid
So the idea behind is try to get the 'difference' in result of the two query. So how many patients in the room went to surgery. What is the best way to use common table expression to get the result?

So how many patients in the room went to surgery.
I suspect you just want conditional aggregation:
select roomId,
count(distinct case when ptprocess = 'surgery' then patientID end) as num_surgery
count(distinct patientID) as total
from data
group by roomId;
Note: I have no idea why you are using count(distinct). Can a patient really occur more than one time in a room?

Related

SQL - count with or without subquery?

I have two tables in my DB:
Building(bno,address,bname) - PK is bno. bno
Room(bno,rno,floor,maxstud) - PK is bno,rno (together)
The Building table stands for a building number, address and name.
The Room table stands for building number, room number, floor number and maximum amount of students who can live in the room.
The query I have to write:
Find a building who has at least 10 rooms, which the maximum amount of students who can live in is 1. The columns should be bno, bname, number of such rooms.
What I wrote:
select building.bno, building.bname, count(rno)
from room natural join building
where maxstud =1
group by bno, bname
having count(rno)>=10
What the solution I have states:
with temp as (
select bno, count(distinct rno) as sumrooms
from room
where maxstud=1
group by bno
)
select bno, bname, sumrooms
from building natural join temp
where sumrooms>=10
Is my solution correct? I didn't see a reason to use a sub-query, but now I'm afraid I was wrong.
Thanks,
Alan
Your query will perform faster but I'm afraid won't compile because you are not including every unaggregated column in the GROUP BY clause (here: building.bname).
Also, the solution that you have which isn't yours counts distinct room numbers, so one may conclude that a building can have several rooms with the same numbers for example on different floors, so that a room would be identified correctly by the unique triple (bno, rno, floor).
Given what I've wrote above your query would look:
select building.bno, building.bname, count(distinct rno)
from room natural join building
where maxstud = 1
group by 1,2 -- I used positions here, you can use names if you wish
having count(distinct rno) >= 10
Your solution is better.
If you are unsure, run both queries on a sample dataset and convince yourself that the results are the same.

PSQL not counting all entries

I am writing a count query which counts attendances at events over a few years. However there is a column named status in which it has extra information about attendance. Apology is listed when the person did not attend and thus should be excluded. status has various entries including group's letters, attendance information, guest status, as well as NULL entries.
The problem is that not all of the attendances are counted in full. They are counted in full when the status NOT LIKE 'Apology' is removed.
How can I get it to count all of the entries? Rather than a limited selection of 364 out of 1583.
I am using psql v9.3.11 in pgAdmin III v1.18.1.
The following is the relevant code:
SELECT b.attendances,
COUNT(b.attendances)
FROM (
SELECT COUNT(a.event_id) as attendances
FROM (SELECT DISTINCT event_id AS event_id,
member_pers_id AS member_pers_id
FROM event_attendance
WHERE status NOT LIKE 'Apology') a
GROUP BY member_pers_id
ORDER BY attendances) b
GROUP BY attendances
ORDER BY attendances ASC
I suspect the NOT LIKE is filtering out additional values, such as NULL. If so, then test for this explicitly.
Also, it makes no sense to do so many aggregations. One is sufficient:
SELECT COUNT(a.event_id) as attendances
FROM (SELECT DISTINCT event_id, member_pers_id
FROM event_attendance
WHERE status NOT LIKE 'Apology' OR status IS NULL
) a
And I'm not sure if the SELECT DISTINCT is necessary

Constructing a query, for selecting a table with limit of associations

I have using the last too many hours trying to construct this sql query that i just can't wrap my head around.
I have three tables, with the following relations, i have removed the rest of the columns for simplicity.
- Jobs
id
- Company
id
- Offer
job_id
company_id
offer_type (either 'single' or 'voucher')
- Reservation
job_id
company_id
Context.
A user creates a job. Companies can make one or two offers (one of each type) on a job, a job is closed when a job gets offers from 3 different companies. Also a reservation can take one of the spots.
So i am trying to fetch all open jobs, for a listing to the company. That is all jobs which have received offers from 2 different companies.
As mentioned i have tried to come up with a query for this, so far i got.
;WITH company_offers AS
(
SELECT
DISTINCT ON(offers.company_id) offers.company_id,
count(offers.company_id) as total,
offers.job_id
FROM offers
GROUP BY offers.company_id, offers.job_id
),
counts AS
(
SELECT jobs.*,
(SELECT count(*) FROM company_offers) as offer_count,
(SELECT count(*) FROM reservations WHERE reservations.job_id = jobs.id) as reservation_count
FROM jobs
JOIN company_offers ON company_offers.job_id = jobs.id
GROUP BY jobs.id
)
SELECT offer_count+reservation_count as total
FROM counts
I have tried to fetch the offers by unique company id, in the first CTE. Then using the second CTE to count the results of the first, and also find the reservation. Then i add them together at last, and lastly i should make a condition that the total is less than 3.
But this doesn't return the expected result, in fact long from.
I would appreciate if someone could help me out, and explain aswell.
Let me know if you got question.
Some generic SQL could look like this:
select Jobs.id
from Jobs
left outer join Offer on Offer.job_id = Jobs.id
left outer join Reservation on Reservation.job_id = Jobs.id
group by Jobs.id
having count(distinct Offer.company_id) + count(distinct Reservation.company_id) < 3
If PostgreSQL does not like that count(distinct ...), you may have to include an equivalent sub-query.
By the way:
SELECT DISTINCT ... GROUP BY ..., i.e. DISTINCT and GROUP BY, usually does not work out.

Grouping Minus Oracle Problems

I've just created this query and I get confuse by the time I grouping this because I can't see them as one grouping. This query runs but not the way I wanted, I want to group the query by the team name but the problem occurs when its query being counted using count(*) and the result of its counting produces the same number ,,,
SELECT TEAM.NAMATEAM, PERSONAL.KODEPERSON
FROM TEAM, PERSONAL
WHERE TEAM.KODETEAM = PERSONAL.KODETEAM
GROUP BY PERSONAL.KODEPERSON, TEAM.NAMATEAM
MINUS
SELECT TEAM.NAMATEAM, PERSONAL.KODEPERSON
FROM TEAM, PERSONAL, AWARD_PERSON
WHERE TEAM.KODETEAM = PERSONAL.KODETEAM
AND AWARD_PERSON.PEMENANG = PERSONAL.KODEPERSON
GROUP BY TEAM.NAMATEAM, PERSONAL.KODEPERSON;
I want to group all these using the team name but using counting will be problem since I have no idea to group within the technique that can be run smoothly as I wanted. Thank you.
Do I understand your question? You are trying to make a table of columns NAMATEAM,X where NAMATEAM are the team names, and X are the number of people on each team who do not have awards (listed in AWARD_PERSON). If so, you should be able to use a sub-select:
SELECT T_NAME, COUNT(*)
FROM (
SELECT TEAM.NAMATEAM "T_NAME", PERSONAL.KODEPERSON
FROM TEAM, PERSONAL
WHERE TEAM.KODETEAM = PERSONAL.KODETEAM
MINUS
SELECT TEAM.NAMATEAM "T_NAME", PERSONAL.KODEPERSON
FROM TEAM, PERSONAL, AWARD_PERSON
WHERE TEAM.KODETEAM = PERSONAL.KODETEAM
AND AWARD_PERSON.PEMENANG = PERSONAL.KODEPERSON )
-- your original query without the GROUP BYs
GROUP BY T_NAME
The first subselect SELECT creates a full list of players, the second subselect SELECT creates a list of players who have won awards (I assume), the MINUS removes the award winners from the full list. Thus the full subselect returns a list of players and their teams, for all players without awards.
The main SELECT then summarizes on the team name only, to yield a per-team count of players without awards.
You should not need your original GROUP BY TEAM.NAMATEAM, PERSONAL.KODEPERSON, unless you have duplicate rows in your database, e.g., one player on one team has more than one row in the database.

SQL - Get answer from query as a single number

The following code returns a couple of numbers, identifying people who take part in more than three activities.
SELECT pnr
FROM Participates
GROUP BY pnr
HAVING count(activities)>3;
I want the answer to be the number of people who participate in more than three activities though, i.e. "4", instead of four unique numbers. What to do?
Access supports derived tables.
SELECT COUNT(*) AS NumberOfParticipants FROM
(
SELECT pnr
FROM Participates
GROUP BY pnr
HAVING count(activities)>3
) T
You will need a WHERE clause on the pnr field to uniquely identify one of your groupings:
SELECT COUNT(pnr)
FROM Participates
GROUP BY pnr
WHERE pnr = 'whatever'
HAVING COUNT(activities)>3
The order of my clauses might be wrong
Select Count(Distinct pnr)
From Participates
Having Count(activities) > 3