Combining results with DateDiff included - sql

I have a list of places where people have been discharged and I need to stratify them by age. The query that I have currently below works, but it lists every discharge type count, by every age.
You can see how for age 0-1-2-3 there are all separate counts of discharge type based on what age. How can I make it so that it's only the dischargeType and the counts that are listed (as long as they are 18 and under)
SELECT DATEDIFF(yyyy, tblVisits.dob, tblVisits.admitdate) AS Age, tblDischarge.dischargeType, COUNT(tblDischarge.dischargeType) AS COUNTS
FROM tblVisits INNER JOIN
tblDischarge ON tblVisitsDischargeStatus = tblDischarge.dis_statID
GROUP BY DATEDIFF(yyyy,tblVisits.dob, tblVisits.Admitdate), tblDischarge.dischargeType
HAVING (DATEDIFF(yyyy,tblVisits.DOB, tblVisits.AdmitDate) <=18)

If you just want people aged under 18, but only one row per discharge-type...
SELECT
tblDischarge.dischargeType,
COUNT(tblDischarge.dischargeType) AS COUNTS
FROM
tblVisits
INNER JOIN
tblDischarge
ON tblVisitsDischargeStatus = tblDischarge.dis_statID
WHERE
(DATEDIFF(yyyy,tblVisits.DOB, tblVisits.AdmitDate) <=18)
GROUP BY
tblDischarge.dischargeType
If you want to group the ages into strata...
SELECT
tblDischarge.dischargeType,
CASE DATEDIFF(yyyy,tblVisits.DOB, tblVisits.AdmitDate)
WHEN <= 3 THEN '0..3'
WHEN <= 8 THEN '4..8'
WHEN <= 12 THEN '9..12'
ELSE '13..18'
END AS ageBand,
COUNT(tblDischarge.dischargeType) AS COUNTS
FROM
tblVisits
INNER JOIN
tblDischarge
ON tblVisitsDischargeStatus = tblDischarge.dis_statID
WHERE
(DATEDIFF(yyyy,tblVisits.DOB, tblVisits.AdmitDate) <=18)
GROUP BY
tblDischarge.dischargeType,
CASE DATEDIFF(yyyy,tblVisits.DOB, tblVisits.AdmitDate)
WHEN <= 3 THEN '0..3'
WHEN <= 8 THEN '4..8'
WHEN <= 12 THEN '9..12'
ELSE '13..18'
END

Remove the AGE in the selection and in the Group by part:
SELECT tblDischarge.dischargeType, COUNT(tblDischarge.dischargeType) AS COUNTS
FROM tblVisits INNER JOIN
tblDischarge ON tblVisitsDischargeStatus = tblDischarge.dis_statID
GROUP BY tblDischarge.dischargeType
HAVING (DATEDIFF(yyyy,tblVisits.DOB, tblVisits.AdmitDate) <=18)

I am not sure of your problem, but if you just want to count by dischargeType, for all people under 18, the code follows:
SELECT dis.dischargeType, COUNT(dis.dischargeType) AS COUNTS
FROM tblDischarge dis
JOIN tblVisits vst ON vst.DischargeStatus = dis.dis_statID
WHERE (DATEDIFF(yyyy,vst.DOB, vst.AdmitDate) <= 18)
GROUP BY dis.dischargeType;
Or you can use sub-query, like:
SELECT dischargeType, COUNT(dischargeType) AS COUNTS
FROM tblDischarge
WHERE dis_statID IN (
SELECT vst.DischargeStatus
FROM tblVisits vst
WHERE (DATEDIFF(yyyy, vst.DOB, vst.AdmitDate) <= 18)
)
GROUP BY dis.dischargeType;

Simply remove the calculation for age from the SELECT and the GROUP BY, and change the HAVING to a WHERE:
SELECT tblDischarge.dischargeType,
COUNT(tblDischarge.dischargeType) as COUNTS
FROM tblVisits
INNER JOIN tblDischarge
ON tblVisitsDischargeStatus = tblDischarge.dis_statID
WHERE (DATEDIFF(yyyy,tblVisits.DOB, tblVisits.AdmitDate) <=18)
GROUP BY tblDischarge.dischargeType

Related

Trying to count number of complaint, cause, corrections

I am trying to create a query with the following fields, that counts the number of Complaints, causes, and corrections show up in the data.
My current error: Select non-aggregate values must be part of the associated group.
I am very new to SQL queries, and am not sure what else I'm missing. All I've done is merged to queries, and seem to be missing something.
select L.Case_ID,
L.Case_Line_ID,
A.Dealer_ID,
M.DealerCode,
H.DealerName,
substr(L.Estimate_Created_At,1,7) as CaseMonth,
count(distinct L.Complaint) as Complaint,
count(distinct C.Cause) as Cause,
count(distinct C.Correction) as Correction
from Decisiv_Tables_Prod.Stg_Decisiv_LineItems L
join Decisiv_Tables_Prod.Stg_Decisiv_Cases A on L.Case_ID = A.Case_ID
join Decisiv_Tables_Prod.Rpt_DecisivDealerMap M on A.Dealer_ID = M.DecisivDealerID
and cast(substr(L.Estimate_Created_At,1,10) as date format 'YYYY-MM-DD') between M.EffectiveStartDate and coalesce(M.EffectiveEndDate, cast('2099-12-31' as date format 'YYYY-MM-DD'))
join Decisiv_Tables_Prod.Rpt_DealerDirectoryHierarchy H on M.DealerCode = H.DealerCode
join Decisiv_Tables_Prod.Stg_Decisiv_LineItems_Clobs C on C.Case_ID = L.Case_ID
and C.Case_Line_ID = L.Case_Line_ID
group by 1,2,3,4,5
Looking to get a table with the following data example:
Dealer ID, Dealer Code, Dealer Name, Case Month, Count of Case_ID, Count of Case_Line_ID, Count of Complaint, Count of Cause, Count of Correction
You have six unaggregated columns:
select L.Case_ID,
L.Case_Line_ID,
A.Dealer_ID,
M.DealerCode,
H.DealerName,
substr(L.Estimate_Created_At,1,7) as CaseMonth,
These should all be in the group by:
group by L.Case_ID, L.Case_Line_ID, A.Dealer_ID,
M.DealerCode, H.DealerName,
substr(L.Estimate_Created_At,1,7) as CaseMonth
As Gordon wrote, the GROUP BY list doesn't match your Select, you need to either remove both Case_ID & Case_Line_ID or aggregate them:
SELECT
A.Dealer_ID,
M.DealerCode,
H.DealerName,
Substr(L.Estimate_Created_At,1,7) AS CaseMonth,
Count(L.Case_ID), -- distinct ?
Count(L.Case_Line_ID), -- distinct ?
Count(DISTINCT L.Complaint) AS Complaint,
Count(DISTINCT C.Cause) AS Cause,
Count(DISTINCT C.Correction) AS Correction
FROM Decisiv_Tables_Prod.Stg_Decisiv_LineItems AS L
JOIN Decisiv_Tables_Prod.Stg_Decisiv_Cases AS A
ON L.Case_ID = A.Case_ID
JOIN Decisiv_Tables_Prod.Rpt_DecisivDealerMap AS M
ON A.Dealer_ID = M.DecisivDealerID
AND Cast(Substr(L.Estimate_Created_At,1,10) AS DATE FORMAT 'YYYY-MM-DD') BETWEEN M.EffectiveStartDate AND Coalesce(M.EffectiveEndDate, DATE '2099-12-31')
JOIN Decisiv_Tables_Prod.Rpt_DealerDirectoryHierarchy AS H
ON M.DealerCode = H.DealerCode
JOIN Decisiv_Tables_Prod.Stg_Decisiv_LineItems_Clobs AS C
ON C.Case_ID = L.Case_ID
AND C.Case_Line_ID = L.Case_Line_ID
GROUP BY
A.Dealer_ID,
M.DealerCode,
H.DealerName,
CaseMonth
I simplified cast('2099-12-31' as date format 'YYYY-MM-DD') to DATE '2099-12-31' and used the column names/aliases in Group By (recommended over 1,2,3,4 in production code).
As Distinct is quite expensive check if you actually need to add it to those counts.

SQL Avoid multiplication on inner joins with several returns

OK, not the best title but could not explain it better.
I have a SQL query with a line like this.
count(PRStatusChangesLog.EffectiveMinutes) as timeInHandoverExternal
it works so far but I also want to add something like this
COUNT (distinct a.ActionId) as 'Number Of Actions',
which requires this
INNER JOIN PRAction a on a.PrId = PRHeader.prid
Now the problem which I am sure some of you have already seen. The previous count is now multiplied by the number of actions.
I can see why this happens but I am not sure how best to do this so I can get both the number of actions and the right count without the multiplier.
Simplified full query
SELECT
PRHeader.PrId,
COUNT (distinct a.ActionId) AS 'Number Of Actions',
COUNT (PRStatusChangesLog.EffectiveMinutes) AS timeInHandoverExternal
FROM
PRHeader
LEFT JOIN
PRStatusChangesLog ON PRStatusChangesLog.PrId = PRHeader.PrId
AND PRStatusChangesLog.StatusId = 4100
INNER JOIN
PRAction a ON a.PrId = PRHeader.prid
WHERE
DATEDIFF(mm, prheader.ClosedDate, getdate()) = 1
AND (PRHeader.siteId = 74)
AND prheader.PRTypeId IN (17, 19)
AND PRHeader.tmpStatusId <> 6010
GROUP BY
PRHeader.PrId
You can count a unique column with DISTINCT like COUNT(DISTINCT PRStatusChangesLog.id).
If this is not possible use a subquery for counting the actions. In the SELECT clause you should write something like: (SELECT COUNT(DISTINCT a.ActionId) FROM ... WHERE PRAction a on a.PrId = PRHeader.prid) AS action_count
Using select statement clause in joins to get counts individually then add with final outer select statement.
SELECT
PRHeader.PrId, Count1 'Number Of Actions', Count2 timeInHandoverExternal
FROM
PRHeader
JOIN
(SELECT COUNT (ActionId) Count1
FROM PRAction
GROUP BY PrId) A ON A.PrId = PRHeader.prid
LEFT JOIN
(SELECT
COUNT(PRStatusChangesLog.EffectiveMinutes) Count2, PrId, StatusId
FROM
PRStatusChangesLog
WHERE
StatusId = 4100
GROUP BY
PrId, StatusId) B ON B.PrId = PRHeader.PrId
WHERE
DATEDIFF(mm, prheader.ClosedDate, getdate()) = 1
AND (PRHeader.siteId = 74 )
AND prheader.PRTypeId IN (17,19)
AND PRHeader.tmpStatusId <> 6010

Display rows that have a zero count

I am trying to display rows even if they return a count of zero. However no luck.
I tried using left join.
select
a.Month,
count(b.InsuranceFromJob) [Number of Participants without Insurance]
from
hsAdmin.ReportPeriodLkup a
left join hsAdmin.ClientReport b on
b.ReportPeriod = a.ReportPeriodId
where
b.insurancefromjob = 2 and
a.reportperiodid between (#lastReportId - 11) and #lastReportId
group by
a.Month
Because clientreport is in the where, only rows that exists in clientreport will be in the resultset.
Move the check to the join and you will get the desired result:
select
a.Month,
count(b.InsuranceFromJob) [Number of Participants without Insurance]
from
hsAdmin.ReportPeriodLkup a
left join hsAdmin.ClientReport b on
b.ReportPeriod = a.ReportPeriodId
and b.insurancefromjob = 2
where
a.reportperiodid between (#lastReportId - 11) and #lastReportId
group by
a.Month

Oracle Max Date query

I have a table called maximo_audit and I am trying to only count the most recent actiondate record with an approvalstatus of N. I wrote this query but it is taking forever to run
SELECT COUNT (*)
FROM (SELECT MAX (actiondate)
FROM timedetail t JOIN maximo_audit m ON m.recordid = t.quantity
WHERE approvalstatus = 'N'
AND t.tsdate BETWEEN '8-dec-2013' AND '21-dec-2013'
AND task2 IS NOT NULL
--and recordid = '1781891'
GROUP BY m.recordid
HAVING MAX (actiondate) IN (SELECT MAX (actiondate)
FROM maximo_audit ma
WHERE ma.recordid = m.recordid))
Often using explicit joins helps the optimizer. You can also replace the subquery with a simpler count(distinct).
SELECT COUNT(DISTINCT m.recordid)
FROM timedetail t JOIN
maximo_audit m
ON m.recordid = t.quantity JOIN
(select ma.recordid, max(actiondate) as actiondate
from maximo_audit ma
group by ma.recordid
) ma
on ma.recordid = m.recordid and ma.actiondate = m.actiondate
WHERE approvalstatus = 'N' AND
t.tsdate BETWEEN '8-dec-2013' AND '21-dec-2013' AND
task2 IS NOT NULL;
The having clause can be replaced with a join because when the maximum of the action date equals something, then one of the action dates will match that value.

SQL: Display associated value based on Min and Max values

I have tblDump which has ~52,000 records. It is a list of IDs with corresponding dosing times. The relevant variables are SubjectNumber, RECDATE, RECTIME, FoodType. Each patient doses twice a day, so there is a Min(RECTIME) and Max(RECTIME) per RECDATE. To display this, I have this so far:
SELECT tblDump.SubjectNumber, tblDump.RECDATE,
Min(RECTIME) As MornDose, Max(RECTIME) As EveDose
FROM tblDump
GROUP BY tblDump.SubjectNumber, tblDump.RECDATE
ORDER BY tblDump.SubjectNumber, tblDump.RECDATE Asc;
For the variable FoodType, there are three possibilities: 1, 2 or 3. I need to print the Subjects whose Min(RECTIME) is associated with a 1 and Max(RECTIME) is associated with a 3, per day.
I was attempting to make the HAVING statement such that the Foodtype = 1 for Min(RecTIME) and Foodtype = 3 for Max(RECTIME), but I can't get the linguistics correct.
Resolved:
Okay, I'm sure there is a more efficient way to do this but this works. Hopefully this will help someone or someone can post a more efficient method. I used 3 queries, one for the Min Dose time, one for the Max dose time, and one to join them together.
qryMin:
SELECT a.SubjectNumber, a.RECDATE, First(a.colm AS MornDose), First(m.FoodType)
FROM (SELECT SubjectNumber, RECDATE, MIN(RECTIME) as colm
FROM tblDump GROUP BY SubjectNumber, RECDATE) AS a
INNER JOIN tblDump AS m
ON (a.colm=m.RECTIME)
AND (a.RECDATE = m.RECDATE)
AND (a.SubjectNumber = m.SubjectNumber)
GROUP BY a.SubjectNumber, a.RECDATE
ORDER BY a.SubjectNumber, a.RECDATE;
qryMax:
SELECT a.SubjectNumber, a.RECDATE, First(a.colm) AS MornDose, First(m.FoodType)
FROM (SELECT SubjectNumber, RECDATE, MAX(RECTIME) AS colm
FROM tblDump GROUP BY SubjectNumber, RECDATE) AS a
INNER JOIN tblDump AS m
ON (a.SubjectNumber = m.SubjectNumber)
AND (a.RECDATE = m.RECDATE)
AND (a.colm = m.RECTIME)
GROUP BY a.SubjectNumber, a.RECDATE
ORDER BY a.SubjectNumber, a.RECDATE;
qryCombo:
SELECT qryMin.*, qryMax.*
FROM qryMin
INNER JOIN qryMax
ON qryMin.SubjectNumber = qryMax.SubjectNumber
WHERE qryMin.RECDATE = qryMax.RECDATE
AND qryMin.FoodType = 1
AND qryMax.FoodType = 3;