Join and max date with null values - sql

I need to return the most recent (max)date for a patient_ID where a vital value exists - it's not simply the max. encounter date for the patient_ID, it must have a corresponding vital value. I also only want encounters where vitals value <>'' and Date >= '2020-01-01' and vital_ID = 232268.
Encounters (enc)
Patient_ID
Encounter_ID
Date
1
11
1/4/2020
1
12
1/15/2020
1
13
3/6/2020
2
14
1/12/2020
3
15
3/16/2020
3
16
4/19/2020
4
17
6/2/2020
4
18
6/12/2020
4
19
9/1/2020
Vitals
Encounter_ID
Vital_ID
Value
11
232268
4.8
12
232268
4.6
14
232268
3.1
16
232268
3.2
17
232268
4.1
18
232268
4.7
Desired Outcome
Patient_ID
Encounter_ID
Date
Value
1
12
3/6/2020
4.6
2
14
1/12/2020
3.1
3
16
4/19/2020
3.2
4
18
9/1/2020
4.7
I tried this, but it returned only the vitals_encounter_ID IF it = max(date) of the encounter for the patient_ID (so did not include patient_ID if vitals were not taken on the max(date) - for instance, it negates patient_ID 1 all together because vitals weren't taken on encounter_ID 13:
select v.encounterID, e.patientID, e.date, v.value, v.vitalID
from vitals v
left join enc e on
e.encounterID = v.encounterID and
v.vitalID = 232268 and
v.value <> '' and
e.date = (select max(date) from enc where patientID=e.patientID)
where e.date >= '2020-01-01'
Cognos 8. I'm new, so please don't eat me alive...

If I follow you correctly, you need the two tables in the correlated subquery:
select v.encounterid, e.patientid, e.date, v.value, v.vitalid
from enc e
inner join vitals v on v.encounterid = e.encounterid
where
v.vitalid = 232268
and v.value <>''
and e.date = (
select max(e1.date)
from enc e1
inner join vitals v1 on v1.encounterid = e1.encounterid
where
e1.patientid = e.patientid
and v1.vitalid = v.vitalid
and v1.value <> ''
and e.date >= '2020-01-01'
)
I don't know if Cognos supports window functions. But if it does, the query can be much simpler phrased:
select *
from (
select v.encounterid, e.patientid, e.date, v.value, v.vitalid,
row_number() over(partition by e.patientid order by e.date)
from enc e
inner join vitals v on v.encounterid = e.encounterid
where v.vitalid = 232268 and v.value <> ''
) t
where rn = 1

Related

SQLite query - Limit occurrence of value

I have a query that return this result. How can i limit the occurrence of a value from the 4th column.
19 1 _BOURC01 1
20 1 _BOURC01 3 2019-11-18
20 1 _BOURC01 3 2017-01-02
21 1 _BOURC01 6
22 1 _BOURC01 10
23 1 _BOURC01 13 2016-06-06
24 1 _BOURC01 21 2016-09-19
My Query:
SELECT "_44_SpeakerSpeech"."id" AS "id", "_44_SpeakerSpeech"."active" AS "active", "_44_SpeakerSpeech"."id_speaker" AS "id_speaker", "_44_SpeakerSpeech"."Speech" AS "Speech", "34 Program Weekend"."date" AS "date"
FROM "_44_SpeakerSpeech"
LEFT JOIN "_34_programWeekend" "34 Program Weekend" ON "_44_SpeakerSpeech"."Speech" = "34 Program Weekend"."theme_id"
WHERE "id_speaker" = "_BOURC01"
ORDER BY id_speaker, Speech, date DESC
Thanks
I think this is what you want here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY s.id, s.active, s.id_speaker, s.Speech
ORDER BY p.date DESC) rn
FROM "_44_SpeakerSpeech" s
LEFT JOIN "_34_programWeekend" p ON s.Speech = p.theme_id
WHERE s.id_speaker = '_BOURC01'
)
SELECT id, active, id_speaker, Speech, date
FROM cte
WHERE rn = 1;
This logic assumes that when two or more records all have the same columns values (excluding the date), you want to retain only the latest record.

Select Where Date Between

I would like to SELECT a table calendar and combine the results with the days of the month.
I mean,
Table: Calendar
ID TEAM EMPLOYER START END
17 19 8 04/08/2014 18:01:00 11/08/2014 07:59:00
18 19 39 11/08/2014 18:01:00 18/08/2014 07:59:00
19 19 44 18/08/2014 18:01:00 25/08/2014 07:59:00
20 19 38 25/08/2014 18:01:00 01/09/2014 07:59:00
And I have a SELECT for the days of the month.
Select Days.Dt
From (Select Trunc(To_Date('2014', 'YYYY'), 'y') - 1 + Rownum Dt
From All_Objects
Where Rownum <= Add_Months(Trunc(To_Date('2014', 'YYYY'), 'y'), 12) -
Trunc(To_Date('2014', 'YYYY'), 'y')) Days
Where To_Char(Dt, 'mm/yyyy') = '08/2014'
What I want is something like this:
DAY EMPLOYER_END EMPLOYER_START
1 01/08/2014
2 02/08/2014
3 03/08/2014
4 04/08/2014 4
5 05/08/2014 4 4
6 06/08/2014 4 4
7 07/08/2014 4 4
8 08/08/2014 4 4
9 09/08/2014 4 4
10 10/08/2014 4 4
11 11/08/2014 4 39
12 12/08/2014 39 39
The employer starts at 18:01 (always) and end at 07:59 (always).
Does anyone know if it's possible?
And the way I can do that.
Thanks!
Your desired results do not match your sample data. However, I think you want something like this:
with dates as (
Select Days.Dt
From (Select Trunc(To_Date('2014', 'YYYY'), 'y') - 1 + Rownum Dt
From All_Objects
Where Rownum <= Add_Months(Trunc(To_Date('2014', 'YYYY'), 'y'), 12) -
Trunc(To_Date('2014', 'YYYY'), 'y')
) Days
Where To_Char(Dt, 'mm/yyyy') = '08/2014'
)
select d.dt,
sum(case when c.employer_start = d.ddt then 0 else 1 end) as employer_end,
sum(case when c.employer_end = d.dt then 1 else 0 end) as employer_start
from dates d left outer join
calendar c
on d.dt between c.employer_start and c.employer_end
group by d.dt
order by d.dt;
I guess this can be useful to you
WITH mindates AS
(SELECT TRUNC(MIN(startdate),'month') st_date,
TRUNC(MAX(enddate)) ed_date
FROM calendar
) ,
dates AS
(SELECT st_date+ rownum-1 AS dates_col
FROM mindates,
dual
CONNECT BY rownum <= (ed_date- st_date)+1
)
SELECT d.dates_col dates,
MIN((
CASE
WHEN d.dates_col=c.startdate
THEN NULL
ELSE c.employer
END)) AS employer_end,
MIN((
CASE
WHEN d.dates_col=c.enddate
THEN NULL
ELSE c.employer
END )) AS employer_start
FROM dates d
LEFT OUTER JOIN calendar c
ON d.dates_col BETWEEN c.startdate AND c.enddate
GROUP BY d.dates_col
ORDER BY d.dates_col;

Group by in subquery?

I need some help with group by in subquery. Can you help me to solve this grouping problem:
select
DATEPART(wk, oh. ExportDate) as wk,
DATEPART(dw,oh.ExportDate) as day,
ro.Name,
Pallets = sum(oh.Pallets),
Box = (select count(Number) from OrderItem where ID_OrderHead = oh.Id)
from
OrderHeadPDAEvent ohpe
left outer join OrderHead oh on oh.Id = ohpe.ID_OrderHeader
left outer join Route ro on oh.ID_Route = ro.ID
where
ID_Route = '00000000-0000-0000-0000-000000000000'
and
oh.ExportDate > dbo.GetStartOfDay('2012-08-01 14:35:00.000' )
and
oh.ExportDate < dbo.GetEndOfDay('2013-08-08 14:35:00.000')
group by
oh.ExportDate, ro.Name, oh.ID
order by
DATEPART(wk, oh. ExportDate)
And data looks like this:
26 3 Standard - Uten rute 5 49
26 3 Standard - Uten rute 2 45
26 3 Standard - Uten rute 2 38
26 3 Standard - Uten rute 1 26
26 3 Standard - Uten rute 1 64
26 3 Standard - Uten rute 2 45
26 3 Standard - Uten rute 3 64
I want to sum all rows to get them in 1 row only.
Try this one -
SELECT
DATEPART(wk, oh.ExportDate) AS wk
, DATEPART(dw, oh.ExportDate) AS day
, ro.name
, Pallets = SUM(oh.Pallets)
, Box = SUM(n)
FROM dbo.OrderHeadPDAEvent ohpe
LEFT JOIN dbo.OrderHead oh ON oh.ID = ohpe.ID_OrderHeader
LEFT JOIN dbo.[Route] ro ON oh.ID_Route = ro.ID
LEFT JOIN (
SELECT n = COUNT(number), ID_OrderHead
FROM dbo.OrderItem
GROUP BY ID_OrderHead
) t ON t.ID_OrderHead = oh.ID
WHERE ID_Route = '00000000-0000-0000-0000-000000000000'
AND oh.ExportDate BETWEEN
dbo.GetStartOfDay('2012-08-01 14:35:00.000')
AND
dbo.GetEndOfDay('2013-08-08 14:35:00.000')
GROUP BY
oh.ExportDate
, ro.name
ORDER BY wk

SQL records only for 3 consecutive months

I have table for emp. attendance sheet:
emp_No Absent_Date
-------------------
111 01/03/2012
111 05/05/2012
222 13/02/2012
222 01/03/2012
222 02/03/2012
222 29/04/2012
222 09/09/2012
333 15/05/2012
333 18/09/2012
333 19/09/2012
I need to return the rows like below:
emp_No Absent_Date
-------------------
222 13/02/2012
222 01/03/2012
222 02/03/2012
222 29/04/2012
because only emp no 222 having absent in 3 consecutive months.
What you are trying to do is to group the absences by consecutive months. Let me assume that you are using a reasonable database that supports the dense_rank() function and basic window functions.
The idea is to find months in sequence that have absences. Then, count the number of months in each sequence for each employee and keep the ones that have more than three months.
The query does this by converting the month to a month number -- 12 times the year plus the month. It then uses a simple observation. The month number minus a sequence of numbers is a constant, for consecutive months. Usually, I use row_number() for the sequence. Because you have duplicate absences in a month, I'm using dense_rank().
select emp_no, absent_date
from (select a.*,
max(monthnum) over (partition by emp_no, groupnum) as lastmonth,
min(monthnum) over (partition by emp_no, groupnum) as firstmonth
from (select a.*,
monthnum - dense_rank() over (partition by emp_no order by monthnum) as groupnum
from (select a.*,
year(a.absent_date)*12+month(a.absent_date) as monthnum
from Attendance a
) a
) a
) a
where lastmonth - firstmonth >= 2
Finally, because you want the absent dates -- as opposed to just the employee numbers -- I find the first and last month using window functions and use their difference as a filter.
I guess the easiest is to do a self join of the table three times, each time adding 1 month to the date:
SELECT DISTINCT S1.emp_No
FROM attendance_sheet S1
JOIN attendance_sheet S2
ON S1.emp_No = S2.emp_No
AND Month(S1.Absent_Date + 1 MONTH) = Month(S2.Absent_Date)
AND Year(S1.Absent_Date + 1 MONTH) = Year(S2.Absent_Date)
JOIN attendance_sheet S3
ON S2.emp_No = S3.emp_No
AND Month(S2.Absent_Date + 1 MONTH) = Month(S3.Absent_Date)
AND Year(S2.Absent_Date + 1 MONTH) = Year(S3.Absent_Date)
This will give you all the unique emp_No's. Now to get the result you want, you'll have to do another join (respectively I'll use IN for easier reading):
SELECT *
FROM attendance_sheet
WHERE emp_No IN (
SELECT S1.emp_No
FROM attendance_sheet S1
JOIN attendance_sheet S2
ON S1.emp_No = S2.emp_No
AND Month(S1.Absent_Date + 1 MONTH) = Month(S2.Absent_Date)
AND Year(S1.Absent_Date + 1 MONTH) = Year(S2.Absent_Date)
JOIN attendance_sheet S3
ON S2.emp_No = S3.emp_No
AND Month(S2.Absent_Date + 1 MONTH) = Month(S3.Absent_Date)
AND Year(S2.Absent_Date + 1 MONTH) = Year(S3.Absent_Date)
)
See the SQL Fiddle to try (I had to change the month adding syntax from standard SQL to MySQL).
Try for this code:
SELECT DISTINCT * FROM
(
SELECT E1.Attendance _No,
E1.Absent_Date
FROM Attendance E1
JOIN Attendance E2
ON E2.Attendance _No = E1.Attendance _No
AND MONTH(E2.Absent_Date) = MONTH(E1.Absent_Date) + 1
JOIN Attendance E3
ON E3.Attendance _No = E2.Attendance _No
AND MONTH(E3.Absent_Date) = MONTH(E2.Absent_Date) + 1
UNION ALL
SELECT E2.Attendance _No,
E2.Absent_Date
FROM Attendance E1
JOIN Attendance E2
ON E2.Attendance _No = E1.Attendance _No
AND MONTH(E2.Absent_Date) = MONTH(E1.Absent_Date) + 1
JOIN Attendance E3
ON E3.Attendance _No = E2.Attendance _No
AND MONTH(E3.Absent_Date) = MONTH(E2.Absent_Date) + 1
UNION ALL
SELECT E3.Attendance _No,
E3.Absent_Date
FROM Attendance E1
JOIN Attendance E2
ON E2.Attendance _No = E1.Attendance _No
AND MONTH(E2.Absent_Date) = MONTH(E1.Absent_Date) + 1
JOIN Attendance E3
ON E3.Attendance _No = E2.Attendance _No
AND MONTH(E3.Absent_Date) = MONTH(E2.Absent_Date) + 1
) A

Query for summation up to certain rows in a table

I have a query like:
select
CONVERT(VARCHAR(7),[startdatetime],111) AS [year-month],
nm.nameLine1,
sum(datediff(hour, startdatetime, enddatetime)) as total
from srl
inner join sr on srl= sr.ServiceRequestId
inner join Name nm on(sr.clientCustomerId = nm.customerId and nm.nameTypeId = 'OFICE')
where (startdatetime >= '08-01-2011 00:00:00.000' and enddatetime <= '10-31-2011 00:00:00.000')
group by nm.nameLine1, [year-month]
order by nm.nameLine1, [year-month]
output of the above query is::
year-month nameLine1 total
---------- --------- -----------
2011/08 B 4
2011/09 B 7
2011/10 B 0
2011/08 E 167
2011/09 E 212
2011/10 E 131
2011/08 L 14
2011/09 L 23
2011/10 L 3
2011/08 O 18
2011/09 O 8
2011/10 O 7
2011/08 S 43
2011/09 S 60
2011/10 S 60
Now my question is, what should I do in the query to get the summation in a different column called nameLine1total for a single nameLine1. The output should be like this:
year-month nameLine1 total nameLine1total
---------- --------- ----------- ---------------
2011/08 B 4
2011/09 B 7
2011/10 B 0 11
2011/08 E 167
2011/09 E 212
2011/10 E 131 510
2011/08 L 14
2011/09 L 23
2011/10 L 3 40
2011/08 O 18
2011/09 O 8
2011/10 O 7 33
2011/08 S 43
2011/09 S 60
2011/10 S 60 163
what variety of sql are you using - sql server, mysql, etc. Also, it doesn't matter for your answer but do you really want <= 10-31-2011 00:00:00, so that data from october 31 isn't included in your query? Also, how are you going to use that query - if it's for a reporting tool, quite likely the reporting tool can calculate it more easily than sql can. If it would be acceptable to have the nameline total included on every row, that would be easier - e.g. if all three rows for "S" said "163" in the last column, is that okay?
Because of convert(.., .., 111) I assume that this is a SQL Server, if it's 2005+ I would use CTE:
with cte (ym, n, total, row) as (
select *, row_number() over(partition by nameLine1 order by nameLine1, [year-month]) from (
select
convert(varchar(7),[startdatetime],111) as [year-month],
nm.nameLine1,
sum(datediff(hour, startdatetime, enddatetime)) as total
from srl
inner join sr on srl= sr.ServiceRequestId
inner join Name nm on
sr.clientCustomerId = nm.customerId and
nm.nameTypeId = 'OFICE'
where startdatetime >= '08-01-2011 00:00:00.000' and enddatetime <= '10-31-2011 00:00:00.000'
group by nm.nameLine1, [year-month]
order by nm.nameLine1, [year-month]
) t
)
select c.ym as [year-month], c.n as [nameLine1], c.total as [total], g.total as [nameLine1total]
from cte c
left join (
select max(row) as row, sum(total) as total, n
from cte group by n
) g on c.n = g.n and c.row = g.row