Filtering data only if criteria is met - sql

I am trying to configure this query that I have but I can't think of a way to do it. My query is below:
select per.Forenames, per.Surname, p.Identifier2
from patient p
join Person per on per.PersonID = p.PersonID
where not exists (select 1
from Episode e
where e.PatientID = p.PatientID and
e.EpisodeTypeID in ('FCB9EAA0-C814-413E-A5FC-48547EF973B7',
'E422A8FA-839B-44AD-9A60-6973FEF39361',
'08929D40-863E-4D46-94BD-B4DF9352A855',
'C8BE80C4-AA0A-41ED-A44C-BCBE2CC980C0',
'8C3848C7-8621-43CF-A58D-D4A6ED4DC166',
'C244B01A-E9DD-4BF4-B336-1479A5A7C88D',
'632FAC1E-6B04-4A69-8BF2-0C2E2B0AD8AB'
) and
e.EpisodeDate between '2016-04-01' and '2016-12-15'
)
and p.PatientStatus = 'Current'
group by p.Identifier2, per.Forenames, per.Surname
So what this query is doing is finding certain people only if they haven't attended the specified episode types. Now what I want to do, is filter this even further. I want to make it so that if they have have attended the episode type with the ID '9254B31D-A304-498C-ADE4-F4003997C8FA' then they should still appear on this list, but only if their attended status is set to 'Yes'. Otherwise I don't want them to show up.
Where can I add this filter?

This type of logic is easier to do using group by and having:
select per.Forenames, per.Surname, p.Identifier2
from patient p join
Person per
on per.PersonID = p.PersonID join
Episode e
on e.PatientID = p.PatientID
group by per.Forenames, per.Surname, p.Identifier2
having sum(case when e.EpisodeTypeID in ('FCB9EAA0-C814-413E-A5FC-48547EF973B7',
'E422A8FA-839B-44AD-9A60-6973FEF39361',
'08929D40-863E-4D46-94BD-B4DF9352A855',
'C8BE80C4-AA0A-41ED-A44C-BCBE2CC980C0',
'8C3848C7-8621-43CF-A58D-D4A6ED4DC166',
'C244B01A-E9DD-4BF4-B336-1479A5A7C88D',
'632FAC1E-6B04-4A69-8BF2-0C2E2B0AD8AB'
) and
e.EpisodeDate between '2016-04-01' and '2016-12-15'
then 1 else 0
end) = 0 and
sum(case when e.EpisodeTypeId = '9254B31D-A304-498C-ADE4-F4003997C8FA' and
e.AttendedStatus <> 'Yes' -- assumes AttendedStatus is not NULL
then 1 else 0
end) = 0;
Each sum() expression counts the number of matches for the conditions

I would just add another condition for and exists if you want an additional condition above and beyond the existing conditions like this:
select per.Forenames, per.Surname, p.Identifier2
from patient p
...
and p.PatientStatus = 'Current'
and exists (
select 1
from Episode e1
where e1.PatientID = p.PatientID
and e1.EpisodeTypeID = '9254B31D-A304-498C-ADE4-F4003997C8FA'
and e1.AttendedStatus = 'Yes'
)
group by ...

Related

Return a Count of 0 When No Rows

OK, I've looked this up and tried a number of solutions, but can't get it to work. I'm a bit of a novice. Here's my original query - how can I get it to return 0 for an account when there are no results in the student table?
SELECT a.NAME
,count(s.student_sid)
FROM account a
JOIN inst i ON a.inst_sid = i.root_inst_sid
JOIN inst_year iy ON i.inst_sid = iy.inst_sid
JOIN student s ON iy.inst_year_sid = s.inst_year_sid
WHERE s.demo = 0
AND s.STATE = 1
AND i.STATE = 1
AND iy.year_sid = 16
AND a.account_sid IN (
20187987
,20188576
,20188755
,52317128
,20189249
)
GROUP BY a.NAME;
Use an outer join, moving the condition on that table into the join:
select a.name, count(s.student_sid)
from account a
join inst i on a.inst_sid = i.root_inst_sid
join inst_year iy on i.inst_sid = iy.inst_sid
left join student s on iy.inst_year_sid = s.inst_year_sid
and s.demo = 0
and s.state = 1
where i.state = 1
and iy.year_sid = 16
and a.account_sid in (20187987, 20188576, 20188755, 52317128, 20189249)
group by a.name;
count() does not count null values, which s.student_sid will be if no rows join from student.
You need to LEFT JOIN and then SUM() over the group where s.student_sid is not null:
select
a.name,
sum(case when s.student_sid is null then 0 else 1 end) as student_count
from account a
join inst i on a.inst_sid = i.root_inst_sid
join inst_year iy on i.inst_sid = iy.inst_sid
left join student s
on iy.inst_year_sid = s.inst_year_sid
and s.demo = 0
and s.state = 1
where i.state = 1
and iy.year_sid = 16
and a.account_sid in (20187987, 20188576, 20188755, 52317128, 20189249)
group by a.name;
This is assuming that all of the fields in the student table that you are filtering on are optional. If you don't want to enforce removal of records where, say, s.state does not equal 1, then you need to move the s.state=1 predicate into the WHERE clauses.
If, for some reason, you are getting duplicate student IDs and students are being counted twice, then you can change the aggregate function to this:
count(distinct s.student_id) as student_count
...which is safe to do as count(distinct ...) ignores null values.

SQL Combining Two Totally seperate tables to one

I am VERY new to SQL and self taught. I have two SQL that I stuggled through but got working. Now I need to combine them into one and I'm lost.
SELECT
s.lastfirst,
s.student_number,
SUM(tr.howmany)
FROM
students s
JOIN
truancies tr ON s.id = tr.studentid
WHERE
s.enroll_status = 0 AND
s.schoolid = ~(curschoolid)
GROUP BY
s.lastfirst, s.student_number
HAVING
SUM(tr.howmany) > 0
ORDER BY
s.lastfirst
And this table:
SELECT
S.DCID as DCID,
S.LASTFIRST as LASTFIRST,
S.STUDENT_NUMBER as STUDENT_NUMBER,
S2.FC_SRVC_HRS_DUE as FC_SRVC_HRS_DUE,
CASE
WHEN S2.FC_SRVC_HRS_COMPLETED IS NULL
THEN '0'
ELSE S2.FC_SRVC_HRS_COMPLETED
END AS FC_SRVC_HRS_COMPLETED,
S2.FC_SRVC_HRS_BUYOUT as FC_SRVC_HRS_BUYOUT,
CASE
WHEN S2.FC_SRVC_HRS_COMPLETED IS NULL
THEN S2.FC_SRVC_HRS_DUE * S2.FC_SRVC_HRS_BUYOUT
ELSE ((S2.FC_SRVC_HRS_DUE - S2.FC_SRVC_HRS_COMPLETED) * S2.FC_SRVC_HRS_BUYOUT)
END as Balance_Due
FROM
STUDENTS S, U_STUDENTSUSERFIELDS S2
WHERE
S.DCID = S2.STUDENTSDCID AND
s.enroll_status = 0 AND
s.schoolid = ~(curschoolid) AND
(((S2.FC_SRVC_HRS_DUE - S2.FC_SRVC_HRS_COMPLETED) * S2.FC_SRVC_HRS_BUYOUT) > 0 OR
((S2.FC_SRVC_HRS_DUE - S2.FC_SRVC_HRS_COMPLETED) * S2.FC_SRVC_HRS_BUYOUT) IS NULL) AND
S2.FC_SRVC_HRS_DUE >.1
ORDER BY
s.lastfirst
What I am really looking for are the totals of both of these. I want the SUM(tr.howmany) from the first table and the balance due of the second BUT I need the filters that are in there. This would be sorted by student. I hope I am making sense. Any assistance would be appreciated.
You can join together 2 separate SQL select statements:
Eg:
Select A.id, A.value, B.value
From (select id, count(*) as value from TableA ...) AS A
join (select id, sum(field) as value from TableB ...) AS B
on A.id = B.id
order by A.id
As long as you have a common field to join on this would work. In your case the student_number looks like a good candidate. You will have to do the ordering outside of your subqueries.

SQL Outer Join views

first of all sorry the question does not describes my problem and I don't know how to tell it as question.
I want to count how many women and men with position supervisor works in every branch.
So this is the SQL:
CREATE VIEW Women (BranchID,AnzahlF,position ) AS
SELECT Branch.BranchID, COUNT(*) As AnzahlF,Staff.position
FROM Staff full outer JOIN
Branch ON Branch.BranchID = Staff.BranchFK
WHERE gender LIKE 'F' AND position LIKE 'Supervisor'
GROUP BY Branch.BranchID,Staff.position
CREATE VIEW Men (BranchID,AnzahlM,position ) AS
SELECT Branch.BranchID,COUNT(*) As AnzahlM ,Staff.position
FROM Staff Full outer JOIN
Branch ON Branch.BranchID = Staff.BranchFK
WHERE gender LIKE 'M' AND position LIKE 'Supervisor'
GROUP BY Branch.BranchID,Staff.position
Select ISNULL(Women.BranchID, Men.BranchID) AS BranchID,
Case When (Women.AnzahlF is Null) THEN 0
ELSE Women.AnzahlF END As ANzahlFSuperv,
Case When (Men.AnzahlM is Null) THEN 0
ELSE Men.AnzahlM END As ANzahlMSuperv
from Women Full outer join Men On Women.BranchID = Men.BranchID
Group by Women.BranchID, Men.BranchID,Women.ANzahlF,Men.AnzahlM Order by BranchID
This is the output:
BranchID,ANzahlFSuperv, ANzahlMSuperv
B001,2,0
B003,1,1
B004,1,1
B005,1,0
B006,1,0
B007,0,2
B008,1,1
B009,0,1
B010,0,1
B011,1,0
B012,0,1
B013,1,0
B014,1,0
=> Missing B002, 0,0
But I do not get the Branch with ID 'B002' it has ANzahlFSuperv = 0 and ANzahlMSuperv = 0. So it has no supersivisor. So how to get this result?
The solution must be in the views so how to get this BranchID.
I tried everything but worthless.
Hope you guys can hep me!
Thanks!
Why would you use views for this? Just a left join and group by:
SELECT b.BranchID, COUNT(*) As AnzahlM,
SUM(CASE WHEN s.gender = 'F' THEN 1 ELSE 0 END) as females,
SUM(CASE WHEN s.gender = 'M' THEN 1 ELSE 0 END) as males
FROM Branch b LEFT JOIN
Staff s
ON b.BranchID = s.BranchFK AND s.position = 'Supervisor'
GROUP BY b.BranchID;

To compute sum regarding to a constraint

I'm using PostgreSQL 8.4.
I have the following sql-query:
SELECT p.partner_id,
CASE WHEN pa.currency_id = 1 THEN SUM(amount) ELSE 0 END AS curUsdAmount,
CASE WHEN pa.currency_id = 2 THEN SUM(amount) ELSE 0 END AS curRubAmount,
CASE WHEN pa.currency_id = 3 THEN SUM(amount) ELSE 0 END AS curUahAmount
FROM public.player_account AS pa
JOIN player AS p ON p.id = pa.player_id
WHERE p.partner_id IN (819)
GROUP BY p.partner_id, pa.currency_id
The thing is that query does not what I expected. I realize that, but now I want to understand what exactly that query does. I mean, what SUM will be counted after the query executed. Could you clarify?
I think you have the conditions backwards in the query:
SELECT p.partner_id,
SUM(CASE WHEN pa.currency_id = 1 THEN amount ELSE 0 END) AS curUsdAmount,
SUM(CASE WHEN pa.currency_id = 2 THEN amount ELSE 0 END) AS curRubAmount,
SUM(CASE WHEN pa.currency_id = 3 THEN amount ELSE 0 END) AS curUahAmount
FROM public.player_account pa JOIN
player p
ON p.id = pa.player_id
WHERE p.partner_id IN (819)
GROUP BY p.partner_id;
Note that I also removed currency_id from the group by clause.
Maybe one row per (partner_id, currency_id) does the job. Faster and cleaner that way:
SELECT p.partner_id, pa.currency_id, sum(amount) AS sum_amount
FROM player_account pa
JOIN player p ON p.id = pa.player_id
WHERE p.partner_id = 819
AND pa.currency_id IN (1,2,3) -- may be redundant if there are not other
GROUP BY 1, 2;
If you need 1 row per partner_id, you are actually looking for "cross-tabulation" or a "pivot table". In Postgres use crosstab() from the additional module tablefunc , which is very fast. (Also available for the outdated version 8.4):
SELECT * FROM crosstab(
'SELECT p.partner_id, pa.currency_id, sum(amount)
FROM player_account pa
JOIN player p ON p.id = pa.player_id
WHERE p.partner_id = 819
AND pa.currency_id IN (1,2,3)
GROUP BY 1, 2
ORDER BY 1, 2'
,VALUES (1), (2), (3)'
) AS t (partner_id int, "curUsdAmount" numeric
, "curRubAmount" numeric
, "curUahAmount" numeric); -- guessing data types
Adapt to your actual data types.
Detailed explanation:
PostgreSQL Crosstab Query

I want SQLplus count(*) to return zero-values

I'm using Oracle 10.2 and have the following query:
select h.company, count(*)
from history h
where h.status = '2'
and h.substatus = '0'
and h.timestamp > '2012-01-01'
and h.timestamp < '2012-02-01'
group by h.company
having (h.company = 'AAA')
or (h.company = 'BBB')
or (h.company = 'CCC')
order by h.company
This will count the amount of times any customer from the companies AAA, BBB or CCC has reached a specific point (status = 2).
Presume no (zero) customers from BBB did so, the result will come back with 2 rows of count(AAA and CCC).
What I want: I wish for the query to return me rows for all 3 companies, even tho the count is zero.
Sorry for the odd layout of the query. It's made to work with MS Excel as well.
Edit: Sorry.. Too little caffeine. changed "Customer" in the later half of the query to "Company".
Clarification: A customer is made unique by combining "h.company" and "h.customer" (or by using the same method in customer-table (customer c), like "c.company" and "c.customer"
Edit 2: Updated code.
select c.company, count(*)
from companyregister c
left join history h on h.company = c.company
where h.status = '2'
and h.substatus = '0'
and h.timestamp > '2012-01-01'
and h.timestamp < '2012-02-01'
group by c.company
having (c.company = 'AAA')
or (c.company = 'BBB')
or (c.company = 'CCC')
order by c.company
Both sets of code from above will yield two rows as follows:
AAA 630
CCC 3020
I would like to have BBB represented, but since they have zero rows in history, they don't show.
Make a left join on the customer table. I don't know what you have named it, but like this:
select c.company, count(h.customer)
from customer as c
left join history as h on h.customer = c.customer
...
Another alternative is to use a condition when counting. I'm not sure if there is any other condition that you need along with the status, but something liket this:
select company, sum(case status when '2' then 1 else 0 end)
from history
where substatus = '0'
and timestamp > '2012-01-01'
and timestamp < '2012-02-01'
and customer in ('AAA', 'BBB', 'CCC')
group by customer
order by customer