Distinct, count, group by query madness - sql

I am trying to return a count of tests taken per term. I can get the count to return, but I can't get it grouped by term.
I've tried everything and the closest I get is grouping by term but then my count only = 1, which isn't right.
Here is what I have now. It just returns a count, how do I group it by term_id?
SELECT COUNT(*)
FROM (SELECT DISTINCT ON(student_id, test_event_id, terf.term_id) student_id
FROM report.test_event_result_fact terf
JOIN report.growth_measurement_window gw on gw.term_id = terf.term_id
JOIN report.term t on t.term_id = terf.term_id
JOIN report.test tt on tt.test_id = terf.test_id
WHERE terf.partner_id = 98
AND growth_event_yn = 't'
AND gw.test_window_complete_yn = 't'
AND gw.growth_window_type = 'DISTRICT'
AND tt.test_type_description = 'SURVEY_WITH_GOALS') as TestEvents

Without knowing more about your setup, that's my best bet:
select term_id, count(*) AS count_per_term
from (
select Distinct on (student_id, test_event_id, terf.term_id)
terf.term_id, student_id
from report.test_event_result_fact terf
join report.growth_measurement_window gw using (term_id)
join report.term t using (term_id)
join report.test tt using (term_id)
where terf.partner_id = 98
and growth_event_yn = 't'
and gw.test_window_complete_yn = 't'
and gw.growth_window_type = 'DISTRICT'
and tt.test_type_description = 'SURVEY_WITH_GOALS') as TestEvents
group by 1;

Related

Return only results that match more than 2 conditions

How do I only return results if more than two values from a list are found?
For example, if they don't have at least three of the following codes attached to their account then don't include them:
I13.0
I13.2
I20.0
I20.1
I20.8
I20.9
I21.01
I21.02
I21.09
I21.11
I21.19
I21.21
I21.29
I21.3
I21.4
I21.9
I21.A1
I21.A9
I22.0
Do I just need to put having count(distinct PROB.ICD10DiagnosisCode) > 2 after the group statement? I am getting results but I want to make sure I am not taking the long way around the bus.
SELECT DISTINCT
PER.ID AS PatientID,
IORG.OrganizationMRN AS PatientMRN
FROM works..Problem PROB (nolock)
JOIN works..Problem_Encounter PROBENC ON PROB.ID = PROBENC.ProblemID
JOIN works..Encounter ENC ON PROBENC.EncounterID = ENC.ID
JOIN works..patient_member PM ON ENC.PatientID = PM.ID
JOIN works..Document ON Document.EncounterID = ENC.ID
JOIN works..DocumentEvent
ON Document.documentid = DocumentEvent.documentid
JOIN works..Person PER ON Document.PatientID = PER.ID
JOIN works..Patient_Iorg IORG
ON PER.ID = IORG.PersonID and IORG.InternalOrganization = 3
WHERE PROB.ProblemStatusDE IN (9)
AND PROB.RecordedDTTM > DATEADD(month,-6,GETDATE())
AND PROB.ICD10DiagnosisCode IN
(
SELECT ICD10DiagnosisCode
FROM CVCReports.dbo.rptPETEligibleICD10
)
AND PM.IsDeceasedFLAG = 'N'
AND DocumentEvent.documenteventde NOT IN (18)
AND Document.Status LIKE 'Final%'
AND Document.DocumentTypeDE NOT IN (288)
AND ENC.DTTM > DATEADD(year,-2,GETDATE())
GROUP BY OrganizationMrn, PER.ID
having count(distinct PROB.ICD10DiagnosisCode) > 2

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.

do a "order by DESC or ASC" in a union with different columns

Good day I need to sort by the Fecha column but when using order by I can not because the select in the union does not have the same columns. how could you sort by this field Fecha. Can you please help me solve this?
e.g. as below.
Below is the query i am using.
SELECT 'PESO:','VOLUMEN:','NO. PIEZAS:','ETD:','ETA:'
UNION ALL
SELECT
(SELECT ISNULL(Valor,'') FROM MovCampoExtra WHERE Modulo = 'VTAS' AND ID = Venta.ID AND CampoExtra = 'CEDIS0103') AS pesoBruto,
(SELECT ISNULL(Valor,'') FROM MovCampoExtra WHERE Modulo = 'VTAS' AND ID = Venta.ID AND CampoExtra = 'CEDIS0121') AS volumen,
(SELECT ISNULL(Valor,'') FROM MovCampoExtra WHERE Modulo = 'VTAS' AND ID = Venta.ID AND CampoExtra = 'CEDIS0102') AS cantidadUnidades,
(SELECT CEDIS0120 FROM VentaCampoExtra WHERE ID = Venta.ID) AS ETD,
(SELECT CEDIS0123 FROM VentaCampoExtra WHERE ID = Venta.ID) AS ETA
FROM Venta
--Venta AS v
INNER JOIN Cte AS c ON c.Cliente = Venta.Cliente
INNER JOIN Usuario us On us.Usuario = Venta.Usuario
WHERE
#Origen=Venta.TMLICentroCosto AND
Venta.Mov IN ('Instruccion EXPO', 'Instruccion IMPO','Instruc. Nacional')
AND Venta.Empresa IN ('TM')
AND Venta.ID is NOT NULL
AND Venta.Estatus NOT IN ('Cancelado')
UNION ALL
SELECT 'FECHA:','HORA:','ACTIVIDADES:',' ',' '
UNION ALL
SELECT
Fecha = ISNULL(CONVERT(VARCHAR, mb.Fecha, 103),''), -- AS Fecha,
Hora = ISNULL(CONVERT(VARCHAR, mb.Fecha, 108),''), -- AS Hora,
Comentarios = ISNULL(mb.Evento2,''),
'',
''
FROM Venta
--Venta AS v
INNER JOIN Cte AS c ON c.Cliente = Venta.Cliente
LEFT OUTER JOIN MovBitacora AS mb ON mb.ID = Venta.ID
INNER JOIN Usuario us On us.Usuario = Venta.Usuario
WHERE
#Origen=Venta.TMLICentroCosto AND
Venta.Mov IN ('Instruccion EXPO', 'Instruccion IMPO','Instruc. Nacional')
AND Venta.Empresa IN ('TM')
AND Venta.ID is NOT NULL
AND Venta.Estatus NOT IN ('Cancelado')
ORDER BY Fecha ASC
RETURN
END
Okay so moaning out the way first, this is a horrible way to do things. You should not be returning header columns from the sql query, and hacking together two unrelated queries. This should be two queries called by the application layer, which should also be handling the headers.
But if you have to do it this way for some arcane reason, you could add a "sorting" column to each query
Header 1 sortval = A
Peso query sortval = B + Peso
Header 2 sortval = C
Fecha query sortval = D + Fecha (in YYYYMMDD format)
Then add a big select round the whole thing to select the columns (which you will need to add aliases for in the first header select) and order by the sortval.

Get the rest of the row in a max group by

I'm trying to acquire the most recently passed training someone has taken. To do this, I have a view that works great
CREATE OR REPLACE FORCE VIEW MYAPP.most_recent_training (
employee_id, course_id, date_taken
) AS SELECT
who.employee_id,
course.course_id,
MAX(sess.end_date) date_taken
FROM employee_session_join esj
JOIN training_session sess on sess.session_id = esj.session_id
JOIN course_version vers on vers.version_id = sess.version_id
JOIN course course on course.course_id = vers.course_id
JOIN employee who on who.employee_id = esj.employee_id
WHERE esj.active_flag = 'Y'
AND sess.active_flag = 'Y'
AND course.active_flag = 'Y'
AND who.active_flag = 'Y'
AND esj.approval_status = 5 -- successfully passed
GROUP BY who.employee_id, course.course_id
Okay, so my query works excellent. Here's my problem - I also need the expiry date so I know when they go out of compliance. This is stored as a number of months on the version. But I can't add vers.valid_for_months because it complains ORA-00979: not a GROUP BY expression.
I just want to get whatever the rest of that row is. How can I do this?
I would think this would solve your problem:
SELECT who.employee_id, course.course_id,
MAX(add_months(sess.end_date, vers.valid_for_months))
That gets the latest end date. If you want the end date for the last session, use row_number():
SELECT employee_id, course_id, end_date
FROM (SELECT who.employee_id, course.course_id, sess.end_date,
row_number() over (partition by who.employee_id, course.course_id
order by sess.end_date
) as seqnum
FROM employee_session_join esj
JOIN training_session sess on sess.session_id = esj.session_id
JOIN course_version vers on vers.version_id = sess.version_id
JOIN course course on course.course_id = vers.course_id
JOIN employee who on who.employee_id = esj.employee_id
WHERE esj.active_flag = 'Y'
AND sess.active_flag = 'Y'
AND course.active_flag = 'Y'
AND who.active_flag = 'Y'
AND esj.approval_status = 5 -- successfully passed
) e
WHERE seqnum = 1;

SQL Union Query

SELECT pv.PropertyID, COUNT(pv.VisitID) AS InitialVisit
FROM tblPAppointments pa INNER JOIN tblPropertyVisit pv ON pv.AppID = pa.AppID
WHERE pv.Status = 0
GROUP BY pv.PropertyID
UNION ALL
SELECT jv.PropertyID, COUNT(jv.JobVistID) AS JobVisit
FROM tblPAppointments pa INNER JOIN tblJobVisits jv ON jv.AppID = pa.AppID
WHERE jv.VisitStatus = 1
GROUP BY jv.PropertyID
I need to get InitialVisit count and JobVisit count in two separate columns.above query returns just two columns (PropertyID,InitialVisit).
Use a NULL as a placeholder for the column that there won't be any output for:
SELECT pv.PropertyID,
COUNT(pv.VisitID) AS InitialVisit,
NULL AS jobvisit
FROM tblPAppointments pa
JOIN tblPropertyVisit pv ON pv.AppID = pa.AppID
WHERE pv.Status = 0
GROUP BY pv.PropertyID
UNION ALL
SELECT jv.PropertyID,
NULL AS initialvisit,
COUNT(jv.JobVistID) AS JobVisit
FROM tblPAppointments pa
JOIN tblJobVisits jv ON jv.AppID = pa.AppID
WHERE jv.VisitStatus = 1
GROUP BY jv.PropertyID
This will return three columns. The column alias is necessary in the first query, but not in the second -- I aliased both to make it clear what is happening.
Be aware that using NULL like this in SQL Server will require you to use CAST/CONVERT on the NULL for data types other than INT because SQL Server defaults the NULL to an INT data type (as odd as that is).
An alternate query that doesn't use UNION:
SELECT x.propertyid,
COUNT(y.visitid) AS initialvisit,
COUNT(z.jobvisitid) AS jobvisit
FROM (SELECT pv.propertyid
FROM TBLPROPERTYVISIT pv
WHERE EXISTS (SELECT NULL
FROM TBLAPPOINTMENTS a
WHERE a.appid = pv.appid)
UNION
SELECT jv.propertyid
FROM TBLJOBVISIT jv
WHERE EXISTS (SELECT NULL
FROM TBLAPPOINTMENTS a
WHERE a.appid = jv.appid)) x
LEFT JOIN TBLPROPERTYVISIT y ON y.propertyid = x.propertyid
LEFT JOIN TBLJOBVISIT z ON z.propertyid = x.propertyid
GROUP BY x.propertyid
No need for a UNION at all. And you don't use tblPAppointments either
Edited to allow for no rows in one of the tables. Still one row output though
SELECT
ISNULL(pv2.PropertyID, jv2.PropertyID),
ISNULL(pv2.InitialVisit, 0),
ISNULL(jv2.JobVisit, 0)
FROM
(
SELECT pv.PropertyID, COUNT(pv.VisitID) AS InitialVisit
FROM tblPropertyVisit pv
WHERE pv.Status = 0
GROUP BY pv.PropertyID
) pv2
FULL OUTER JOIN
(
SELECT jv.PropertyID, COUNT(jv.JobVistID) AS JobVisit
FROM tblJobVisits jv
WHERE jv.VisitStatus = 1
GROUP BY jv.PropertyID
) jv2 ON pv2.PropertyID = jv2.PropertyID