compare count columns not recognized - sql

I have some Columns whose values depend on sums for the row info. However, when I get to the where statement, it won't let me compare those values so I can just display the ones with issues. See comment *** in query for issue.
This is what my query looks like:
SELECT
DISTINCT
p.ID
,p.Last
,p.First
,SUM(distinct CASE WHEN pf.facility in ('S','H','E') then 1 ELSE 0 END) as facility_count
,SUM(distinct CASE WHEN (fs.ques_id = 59 AND pf.facility in ('S','H','E') ) THEN 1 ELSE 0 END) AS sum_qst_59
FROM person p
inner JOIN person_facilities pf ON p.ID = pf.ID
LEFT JOIN dbo.ADD_ANSW fs ON p.id = fs.id
WHERE
pf.Facility in ('s', 'h', 'e')
AND
pf.status in ('Active')
AND
facility_count != sum_qst_59 ***--this doesn't work. Says no such columns***
group by p.id, Last, First
order by Last
I saw compare counts, but I can't figure out how to get that to work for my query and output. Any help would be appreciated.
In other words, there are people that belong in multiple facilities (which I'm counting their facilities for each person). Plus I'm counting how many times they have answer 59 for those same facilities. Then I'm comparing the counts because they should be the same if they answered it for all facilities. Then I'm finding where the counts don't match so we can fix it.
*Update -
I'm trying what was suggested but it has the error
Incorrect syntax near keyword 'having'
SELECT
DISTINCT
p.ID
,p.Last
,p.First
--change in having and sum line below
,having SUM(distinct CASE WHEN pf.facility in ('S','H','E') then 1 ELSE 0 END) as facility_count
<>
SUM(distinct CASE WHEN (fs.ques_id = 59 AND pf.facility in ('S','H','E') ) THEN 1 ELSE 0 END) AS sum_qst_59
FROM person p
inner JOIN person_facilities pf ON p.ID = pf.ID
LEFT JOIN dbo.ADD_ANSW fs ON p.id = fs.id
WHERE
pf.Facility in ('s', 'h', 'e')
AND
pf.status in ('Active')
group by p.id, Last, First
order by Last
Update 2: Also tried the following but still incorrect syntax near having -
SELECT
DISTINCT
p.ID
,p.Last
,p.First
,SUM(distinct CASE WHEN pf.facility in ('S','H','E') then 1 ELSE 0 END) as facility_count
,SUM(distinct CASE WHEN (fs.ques_id = 59 AND pf.facility in ('S','H','E') ) THEN 1 ELSE 0 END) AS sum_qst_59
FROM person p
inner JOIN person_facilities pf ON p.ID = pf.ID
LEFT JOIN dbo.ADD_ANSW fs ON p.id = fs.id
WHERE
pf.Facility in ('s', 'h', 'e')
AND
pf.status in ('Active')
--change in having and sum line below
AND --this has incorrect syntax near having
having SUM(distinct CASE WHEN pf.facility in ('S','H','E') then 1 ELSE 0 END)
<>
SUM(distinct CASE WHEN (fs.ques_id = 59 AND pf.facility in ('S','H','E') ) THEN 1 ELSE 0 END)
group by p.id, Last, First
order by Last

Column aliases cannot be used to reference expressions in a WHERE clause. But even if they could you'd be wrong as filters that should take place after aggregation have to be put in a HAVING clause.
Try:
...
HAVING sum(CASE
WHEN pf.facility IN ('S','H','E') THEN
1
ELSE
0
END)
<>
sum(CASE
WHEN fs.ques_id = 59
AND pf.facility IN ('S','H','E') THEN
1
ELSE
0
END)
...
sum(DISTINCT ...) also doesn't seem to make sense. And the DISTINCT in the outer SELECT also could be useless.

Related

I need to pull unique patients that meet certain criteria

I pulled person_nbrs that have never had an EventType1 before or after an EventType2. I need to pull person_nbrs that have not had an EventType1 prior to having an EventType2. If they had an EventType1 after an EventType2, than it is to be ignored. Here is my query that pulls person_nbrs that have never had an EventType1 before or after EventType2.
SELECT
person_nbr, enc_nbr, enc_timestamp
FROM
person p
JOIN
patient_encounter pe ON p.person_id = pe.person_id
JOIN
patient_procedure pp ON pe.enc_id = pp.enc_id
WHERE
enc_timestamp >= '20170101'
--EventType2
AND code_id LIKE '2'
-- EventType1
AND person_nbr NOT IN (SELECT person_nbr
FROM person p
JOIN patient_encounter pe ON p.person_id = pe.person_id
JOIN patient_procedure pp ON pe.enc_id = pp.enc_id
WHERE code_id LIKE '1')
GROUP BY
person_nbr, enc_nbr, enc_timestamp
ORDER BY
person_nbr ;
You can do this with aggregation and a HAVING clause:
SELECT p.person_nbr
FROM person p JOIN
patient_encounter pe
ON p.person_id = pe.person_id JOIN
patient_procedure pp
ON pe.enc_id = pp.enc_id
GROUP BY p.person_nbr
HAVING SUM(CASE WHEN pp.code_id = 2 THEN 1 ELSE 0 END) > 0 AND -- has code 2
(MAX(CASE WHEN pp.code_id = 1 THEN pe.timestamp END) IS NULL OR
MAX(CASE WHEN pp.code_id = 1 THEN pe.timestamp END) < MIN(CASE WHEN pp.code_id = 2 THEN pe.timestamp END)
) ;
The HAVING clause has two parts:
The first specifies that the person has a code = 2.
The second specifies one of two conditions. The first is that there is no code = 1. The second alternative is that the latest c = 1 timestamp is less than the earliest code = 2 timestamp.

Group by with case when

I have Projects - Issues, one to many relationship.
I want Pending issues and Completed issues for each project.
So, what I have done
SELECT
a.id ,
a.Name,
SUM(CASE WHEN b.StatusId = 3 THEN 1 ELSE NULL END) AS CompletedIssues,
SUM(CASE WHEN b.StatusId != 3 THEN 1 ELSE NULL END) AS PendingIssues
FROM
Projects a
JOIN Issues b
ON a.ID = b.ProjectId
GROUP BY
a.name,
b.StatusId,
a.ID
But it's not giving proper output. see below snap.
There are two separate rows for Completed and pending issues and sometimes more then 2 rows based upon Issues Status ID (See BT5).
Is case when is wrong for this scenario?
what is the proper way to achieve this?
Fix your group by:
select p.id, p.Name,
sum(case when i.StatusId = 3 then 1 else null end) as CompletedIssues,
sum(case when i.StatusId <> 3 then 1 else null end) as PendingIssues
from Projects p join
Issues i
on p.ID = i.ProjectId
group by p.name, p.id;
Note: You may not want else NULL. Normally, you want counts to be zero rather than NULL:
select p.id, p.Name,
sum(case when i.StatusId = 3 then 1 else 0 end) as CompletedIssues,
sum(case when i.StatusId <> 3 then 1 else 0 end) as PendingIssues
from Projects p join
Issues i
on p.ID = i.ProjectId
group by p.name, p.id;
Also, I changed the table aliases to something more meaningful. Don't use meaningless letters such as a and b. Use table abbreviations.

Sum values are inflated when I join another table

I have two queries that return the result sets I want, it is one row per user per day. The problem is that when I try to join the two queries, by userid, I get inflated results because the user is in the table multiple times for each day. How do I join them and avoid having the inflated results?
**Query 1**
SELECT AAL.UserID
, SUM(AAL.Dur)/60 AS 'LIM'
, SUM(CASE When AAL.DUR = 'av' then AAL.Dur/60
Else 0 END) AS 'AVAIL'
FROM WG
INNER JOIN AAL
on WG.UserID=AAL.UserID
and WG.SiteID=AAL.SiteID
WHERE WG.WG = 'OP'
AND DATEDIFF(day,AAL.Date,GETDATE()) = 1
GROUP BY AAL.UserID
**Query 2**
SELECT R.UserID
, SUM(CASE When R.StID = 4 then 1
Else 0 End) AS 'Rf Ct'
FROM R
INNER JOIN WG
on R.UserID = WG.UserID
WHERE WG.WG = 'OP'
AND DATEDIFF(day,R.Date,GETDATE()) = 1
GROUP BY R.UserID
**JOIN ATTEMPT**
SELECT AAL.UserID
, SUM(AAL.Dur)/60 AS 'LIM'
, SUM(CASE When AAL.DUR = 'av' then AAL.Dur/60
Else 0 END) AS 'AVAIL'
, SUM(CASE When R.StID = 4 then 1
Else 0 End) AS 'Rf Ct'
FROM WG
INNER JOIN AAL
on WG.UserID=AAL.UserID
and WG.SiteID=AAL.SiteID
INNER JOIN R
on AAL.UserID=R.UserID
WHERE WG.WG = 'OP'
AND DATEDIFF(day,AAL.Date,GETDATE()) = 1
GROUP BY AAL.UserID
You are not using WB in the query. So, just use exists or in:
SELECT R.UserID, SUM(CASE When R.StID = 4 then 1 Else 0 End) AS 'Rf Ct'
FROM R
WHERE DATEDIFF(day, R.Date, GETDATE()) = 1 AND
EXISTS (SELECT 1 FROM WB WHERE R.UserID = WG.UserID AND WG.WG = 'OP')
GROUP BY R.UserID;
You should understand the reason why the number gets multiplied -- multiple rows in WG correspond to a single R.ID.

SQL Server 2012 - is there a better way to do this as when there are duplicates it counts them more than once?

This is not accurate as the count can be wrong so is there a better way using exists? I want to identify if one case of each course exists.
SELECT
IdentityCourses.IdentityID AS ID,Identities.LastName AS LastName,
Identities.FirstNames AS FirstName,Units.UnitID, Units.Description AS Unit
FROM
dbo.UnitIdentities
INNER JOIN
dbo.IdentityCourses ON dbo.UnitIdentities.IdentityID = dbo.IdentityCourses.IdentityID
INNER JOIN
dbo.COCSourceCourses ON dbo.IdentityCourses.CourseID = dbo.COCSourceCourses.CBESCourseID
INNER JOIN
dbo.Identities ON dbo.UnitIdentities.IdentityID = dbo.Identities.IdentityID
INNER JOIN
dbo.Units ON dbo.UnitIdentities.UnitID = dbo.Units.UnitID
WHERE
(dbo.UnitIdentities.IsActiveMember = 1)
GROUP BY
IdentityCourses.IdentityID, Identities.LastName, Identities.FirstNames,
Units.Description, Units.UnitID
HAVING
(SUM((CASE WHEN COCSourceCourses.COCID = 10048 then 1 else 0 end)+
(CASE WHEN COCSourceCourses.COCID = 10049 then 1 else 0 end)+
(CASE WHEN COCSourceCourses.COCID = 10050 then 1 else 0 end)+
(CASE WHEN COCSourceCourses.COCID = 10051 then 1 else 0 end)+
(CASE WHEN COCSourceCourses.COCID = 10063 then 1 else 0 end)+
(CASE WHEN COCSourceCourses.COCID = 10073 then 1 else 0 end))) = 6
AND IdentityCourses.IdentityID NOT IN (SELECT IdentityID
FROM IdentityQualifications
WHERE QualificationID IN (1012, 1014, 1025))
ORDER BY
Units.UnitID
Try using count(distinct ..):
SELECT (..columns..)
FROM dbo.UnitIdentities UI
LEFT JOIN IdentityQualifications IQ
ON IQ.IdentityID = UI.IdentityID
AND IQ.QualificationID IN (1012, 1014, 1025)
INNER JOIN dbo.IdentityCourses IC
ON IC.IdentityID = dbo.UnitIdentities.IdentityID
INNER JOIN dbo.COCSourceCourses COC
ON COC.CBESCourseID = IC.CourseID
AND COC.COCID IN (10048, 10049, 10050, 10051, 10063, 10073)
(..two more table joins on identities and units..)
WHERE IQ.IdentityID IS NULL
GROUP BY (..columns..)
HAVING COUNT(DISTINCT COC.COCID) = 6
ORDER BY Units.UnitID
When you are only interested in certain records, then why don't you use the WHERE clause? Only select the COCIDs you are interested in and then count distinct results.
You don't need any GROUP BY and HAVING by the way, as you only display identities/units, so you can count associated courses in a subquery in your WHERE clause.
select
i.identityid as id,
i.lastname as lastname,
i.firstnames as firstname,
u.unitid,
u.description as unit
from dbo.identities i
join dbo.unitidentities ui on ui.identityid = i.identityid and ui.isactivemember = 1
join dbo.units u on u.unitid = ui.unitid
where i.identityid not in
(
select iq.identityid
from identityqualifications iq
where iq.qualificationid in (1012, 1014, 1025)
)
and
(
select count(distinct sc.cocid)
from dbo.cocsourcecourses sc
join dbo.identitycourses ic on ic.courseid = sc.cbescourseid
where sc.cocid in (10048, 10049, 10050, 10051, 10063, 10073)
and ic.identityid = i.identityid
) = 6
order by u.unitid;

HAVING clause on SUM column

I want to have a condition on my score column that I get from sum, but HAVING score =< 1 is not working if I put it after group by. That would have to show me projects that have good score. I am using hsqldb, what's going wrong? I get 'user lacks privelege or object not found: SCORE'
SELECT p.id, p.project_name, SUM(CASE r.type_code
WHEN 'GOOD' THEN 1
WHEN 'VERY_GOOD' THEN 1
WHEN 'BAD' THEN -1
WHEN 'VERY_BAD' THEN -1
ELSE 0 END) AS score
FROM record_project AS rp
JOIN project AS p ON p.id = rp.project_id
JOIN record AS r ON r.id = rp.record_id
GROUP BY p.id, p.project_name
HAVING score =< 1 <<<---- wrong?!
ORDER BY score DESC LIMIT 1
You should be using the whole calculated column,
SELECT p.id, p.project_name,
SUM(CASE WHEN r.type_code IN ('GOOD','VERY_GOOD') THEN 1
WHEN r.type_code IN ('BAD','VERY_BAD') THEN -1
ELSE 0 END) score
FROM record_project AS rp
JOIN project AS p ON p.id = rp.project_id
JOIN record AS r ON r.id = rp.record_id
GROUP BY p.id, p.project_name
HAVING SUM(CASE WHEN r.type_code IN ('GOOD','VERY_GOOD') THEN 1
WHEN r.type_code IN ('BAD','VERY_BAD') THEN -1
ELSE 0 END) <= 1
ORDER BY score DESC
-- LIMIT 1
You can incorporate the HAVING as a WHERE over a subquery:
SELECT * FROM (
SELECT p.id, p.project_name, SUM(CASE r.type_code
WHEN 'GOOD' THEN 1
WHEN 'VERY_GOOD' THEN 1
WHEN 'BAD' THEN -1
WHEN 'VERY_BAD' THEN -1
ELSE 0 END) AS score
FROM record_project AS rp
JOIN project AS p ON p.id = rp.project_id
JOIN record AS r ON r.id = rp.record_id
GROUP BY p.id, p.project_name) x
WHERE score =< 1
ORDER BY score DESC
LIMIT 1