Postgresql - retrieve rows within criteria within 30 day span - sql

I have the following tables
AdmittedPatients(pid, workerid, admitted, discharged)
Patients(pid, firstname, lastname, admitted, discharged)
DiagnosticHistory(diagnosisID, workerid, pid, timeofdiagnosis)
Diagnosis(diagnosisID, description)
Here is an SQL Fiddle: http://sqlfiddle.com/#!15/e7403
Things to note:
AdmittedPatients is a history of all admissions/discharges of patients at the hospital.
Patients contain all patients who have records at the hospital. Patients also lists who are currently staying at the hospital (i.e. discharged is NULL).
DiagnosticHistory contains all diagnosis made.
Diagnosis has the description of the diagnosis made
Here is my task: list patients who were admitted to the hospital within 30 days of their last discharge date. For each patient list their patient identification number, name, diagnosis, and admitting doctor.
This is what I've cooked up so far:
select pid, firstname, lastname, admittedpatients.workerid, patients.admitted, admittedpatients.discharged
from patients
join admittedpatients using (pid)
group by pid, firstname, lastname, patients.admitted, admittedpatients.workerid, admittedpatients.discharged
having patients.admitted <= admittedpatients.discharged;
This returns pid's from 0, 1, and 4 when it should 0, 1, 2, and 4.

Not sure why out need group by or having here... no aggregate...
SELECT A.pid, firstname, lastname, A.workerid, P.admitted, A.discharged
FROM patients P
INNER JOIN admittedpatients A
on P.pID = A.pID
WHERE date_add(a.discharged, interval 30 day)>=p.admitted
and p.admitted >=a.discharged
updated fiddle: http://sqlfiddle.com/#!2/dc33c/30/0
Didn't get into returning all your needed fields but as this gets the desired result set I imagine it's just a series of joins from here...
Updated to postgresql:
SELECT A.pid, firstname, lastname, A.workerid, P.admitted, A.discharged
FROM patients P
INNER JOIN admittedpatients A
on P.pID = A.pID
WHERE a.discharged+ interval '30 day' >=p.admitted
and p.admitted >=a.discharged
http://sqlfiddle.com/#!15/e7403/1/0

I didn't see any diagnostic info in the fiddle, so I didn't return any.
select pid
,p.lastname,p.firstname
,ad.lastname,ad.firstname
from AdmittedPatients as a
join AdmittedPatients as d using (pid)
join Patients as p using (pid)
join AdminDoctors as ad on ad.workerid=a.workerid
where d.discharged between a.admitted-30 and a.admitted

You have a rather basic WHERE clause error here:
Admitted cannot be both before discharged AND after discharged+30
Also you have an extra semicolon before your whole query is ended, probably throwing out the last line altogether.
I think you're looking for admitted=discharged

Related

SQL COUNT By month

I have the following table that registers the days students went to school. I need to count the days they are PRESENT, but also need to count the total of school days for each month. (When ASISTENCIA is either 0 or 1)
This what I have so far, but it doesn't count the total.
BEGIN
SELECT
u.user_id user_id,
u.user_first_name as names,
u.user_last_name_01 as lastname1,
u.user_last_name_02 as lastname2,
MONTH(a.FECHA_ASISTENCIA) month,
COUNT(*) as absent_days,
p.PHONE as phone,
p.CITY as city,
#EDUCATION_LEVEL_ID
FROM
users u
inner join asistencia a ON u.user_id = a.USER_ID
inner join profile p ON u.rut_SF = p.RUT_SF
WHERE
a.ASISTENCIA = 0 -- NOT PRESENT
AND a.EDUCATION_LEVEL_ID = #EDUCATION_LEVEL_ID
AND YEAR(a.FECHA_ASISTENCIA) = #EDUCATION_LEVEL_YEAR
GROUP BY
u.user_id,
u.user_first_name,
u.user_last_name_01,
u.user_last_name_02,
MONTH(a.FECHA_ASISTENCIA),
p.TELEFONO,
p.CIUDAD_DOM
ORDER BY mes
END
ATTENDANCE
USER_ID
DATE
ATTENDANCE
EDUCATION_LEVEL_ID
123
2021-04-13
0
1
123
2021-04-14
1
1
DESIRED OUTPUT
names
lastname1
lastname2
month
absent_days
total_class_days
city
JOHN
SMITH
SMITH
3
10
24
CITY
JOHN
SMITH
SMITH
4
8
24
CITY
Without examples of what these tables look like, it is hard to give you a solid answer. However, it appears as though your biggest challenge is that ABSTENTIA does not exist.
This is a common problem for analysis - you need to create rows that do not exist (when the user was absent).
The general approach is to:
create a list of unique users
create a list of unique dates you care about
Cross Join (cartesian join) these to create every possible combination of user and date
Outer Join #3 to #4 so you can populate a PRESENT flag, and now you can see both who were PRESENT and which were not.
Filter out rows which don't apply (for instance if a user joined on 3/4/2021 then ignore the blank rows before this date)
You can accomplish this with some SQL that looks like this:
WITH GLOBAL_SPINE AS (
SELECT
ROW_NUMBER() OVER (ORDER BY NULL) as INTERVAL_ID,
DATEADD('DAY', (INTERVAL_ID - 1), '2021-01-01'::timestamp_ntz) as SPINE_START,
DATEADD('DAY', INTERVAL_ID, '2022-01-01'::timestamp_ntz) as SPINE_END
FROM TABLE (GENERATOR(ROWCOUNT => 365))
),
GROUPS AS (
SELECT
USERID,
MIN(DESIRED_INTERVAL) AS LOCAL_START,
MAX(DESIRED_INTERVAL) AS LOCAL_END
FROM RASGO.PUBLIC.RASGO_SDK__OP4__AGGREGATE_TRANSFORM__8AB1FEDF90
GROUP BY
USERID
),
GROUP_SPINE AS (
SELECT
USERID,
SPINE_START AS GROUP_START,
SPINE_END AS GROUP_END
FROM GROUPS G
CROSS JOIN LATERAL (
SELECT
SPINE_START, SPINE_END
FROM GLOBAL_SPINE S
WHERE S.SPINE_START BETWEEN G.LOCAL_START AND G.LOCAL_END
)
)
SELECT
G.USERID AS GROUP_BY_USERID,
GROUP_START,
GROUP_END,
T.*
FROM GROUP_SPINE G
LEFT JOIN {{ your_table }} T
ON DESIRED_INTERVAL >= G.GROUP_START
AND DESIRED_INTERVAL < G.GROUP_END
AND G.USERID = T.USERID;
The above script works on Snowflake, but the syntax might be slightly different depending on your RDBMS. There are also some other tweaks you can make regarding when you insert the blank rows, I chose 'local' which means that we begin inserting rows for each user on their very first day. You could change this to global if you wanted to populate data from every single day between 1/1/2021 and 1/1/2022.
I used Rasgo to generate this SQL

MS Access SQL - Most Recent Record for Each Consultant ID

I have spent some time on StackOverflow looking for this answer and trying a bunch of these solutions without luck. I feel like I am missing something minor but I cannot resolve it. NOTE - Learning SQL and using Access because that is what work uses.
I have 2 tables, 1 has consultant information in it (Columns:Consultant ID, First Name, Last Name, Active (Yes or No Checkbox). The 2nd table has their Weekly Numbers (Columns: AutoGenID, Consultant ID, WeekOf (Date), FullService, Consulting, Classified, Reallocations, RecruitmentSrvs) and joined them on ConsultantID (primary key)
I built a simply query to Join the 2 tables and show all the results ONLY for the active consultants in qry_Join (anyone marked not active does not show in this query) qry_Join returns Consultant ID, First Name, & Last Name (From tbl_Consultants) and then WeekOf (Date), FullService, Consulting, Classified, Reallocations, RecruitmentSrvs from tbl_WeeklyNumbers.
Question:
I would like to have a query that shows ONLY the most recent WeekOf (Date)entry by each consultant.
Issue:
SQL I am using is below but the issue I am having is that if ConsultantID #'s 3, 4, 5, 6, & 7 use a 10/11/2017 date and then ConsultantID #8 uses a 10/12/2017, the query will only return ConsultantID #8's row since it is most recent. I need it to still return all the other consultants most recent rows as well even if they are a date prior to ConsultantID #8s'
SQL:
SELECT ConsultantID, FirstName, WeekOf, USFullService, USConsulting, Classified, Reallocations, RecruitmentSrvs
FROM qry_Join
Where WeekOf = (SELECT MAX(WeekOf) FROM qry_Join)
Just pass ConsultantID to your subquery:
SELECT ConsultantID, FirstName, WeekOf, USFullService, USConsulting, Classified, Reallocations, RecruitmentSrvs
FROM qry_Join q
Where WeekOf = (SELECT MAX(s.WeekOf) FROM qry_Join s WHERE s.ConsultantID = q.ConsultantID)
SELECT ConsultantID,
FirstName,
WeekOf,
USFullService,
USConsulting,
Classified,
Reallocations,
RecruitmentSrvs
FROM qry_Join INNER JOIN
(SELECT MAX(WeekOf) maxwkof ,ConsultantID cid
FROM qry_Join
GROUP BY ConsultantID )
ON ConsultantID = cid
WHERE maxwkof = WeekOf
The issue in your query is, The below query gives a max date in the whole table. And that max id Matches only one consultant. SO you get one row
WeekOf = (SELECT MAX(WeekOf) FROM qry_Join)
The below query will give the max date of each consultant. SO you can join the max date and Consultant ID to get the details for each consultant in their latest weekoff
SELECT MAX(WeekOf) maxwkof ,ConsultantID cid
FROM qry_Join
GROUP BY ConsultantID

PostgreSQL show latest date and one value of a column from many

I have a problem with a query, that i can't figure out. Have tried for some time, but I just can't figure it out. Would be a great deal of help if you could help me. So... I have 4 tables:
cars - ID, make, model, plate_number, price, type, year, owner_ID
persons - ID, name, surname, pers_code
insurance_data - company_ID, car_ID, first_date, last_date
companies - ID, title
My query so far is..
SELECT cars.plate_number, persons.name, persons.surname, insurance_data.last_date
FROM cars,persons,insurance_data
WHERE cars.owner_ID = persons.ID AND cars.ID = insurance_data.car_ID
This outputs cars plate number, owner of the car, and the last date of the car's insurance. But the problem is that there's two cars that have two end dates of insurance, so in the output there's two entries for same car and with both insurance end dates. What i need is that there would be only one entry for each car and corresponding insurance end date should be the latest.
I know this is pretty basic, but i'm a first year student of databases, and this is one of my first assignments. Thanks in advance
(1) Never use commas in the FROM clause. Always use proper, explicit JOIN syntax.
(2) Use table aliases!
The answer to your question is DISTINCT ON:
SELECT DISTINCT ON (c.plate_number) c.plate_number, p.name, p.surname, id.last_date
FROM cars c JOIN
persons p
ON c.owner_ID = p.ID JOIN
insurance_data id
ON c.ID = id.car_ID
ORDER BY c.plate_number, id.last_date DESC;

Need to pick up tuples that have got different values in other table SQL

I have three tables:
Section (SectID, Description, State)
Employee (EmpID, SectID, Fname, Gname)
JobDone (JobID, EmpID*)
Each Job can be done by several employees and each Section can include several employees.
I need to pick up all Jobs that are done by more that 4 employees and all these employees come from different sections
I've got the following SQL query that should show (haven't tested I yet) EmpID, JobID and SectID for the Jobs that require more than 4 employees but I can't figure out how to show Jobs that have all different Sections
select EmpID, JobID, SectID
from JobDone natural join Employee
where JobID in
(select JobDone.JobID
from JobDone
group by JobDone.JobID
having count(*) > 4
)
Any help would be much appreciated
Thanks
Here is a query to get all the jobids that have at least 4 employees in at least 4 different sections. Since each employee works in the particular section, 4 different sections have at least 4 employees.
select JobID
from JobDone j
join Employee e
on j.EmpID = e.EmpID
group by JobID
having count(distinct e.SectID)>=4
Us above query instead of the sub query in your code.

SQL Server 2008: Using Multiple dts Ranges to Build a Set of Dates

I'm trying to build a query for a medical database that counts the number of patients that were on at least one medication from a class of medications (the medications listed below in the FAST_MEDS CTE) and had either:
1) A diagnosis of myopathy (the list of diagnoses in the FAST_DX CTE)
2) A CPK lab value above 1000 (the lab value in the FAST_LABS CTE)
and this diagnosis or lab happened AFTER a patient was on a statin.
The query I've included below does that under the assumption that once a patient is on a statin, they're on a statin forever. The first CTE collects the ids of patients that were on a statin along with the first date of their diagnosis, the second those with a diagnosis, and the third those with a high lab value. After this I count those that match the above criteria.
What I would like to do is drop the assumption that once a patient is on a statin, they're on it for life. The table edw_dm.patient_medications has a column called start_dts and end_dts. This table has one row for each prescription written, with start_dts and end_dts denoting the start and end date of the prescription. End_dts could be null, which I'll take to assume that the patient is currently on this medication (it could be a missing record, but I can't do anything about this). If a patient is on two different statins, the start and ends dates can overlap, and there may be multiple records of the same medication for a patient, as in a record showing 3-11-2000 to 4-5-2003 and another for the same patient showing 5-6-2007 to 7-8-2009.
I would like to use these two columns to build a query where I'm only counting the patients that had a lab value or diagnosis done during a time when they were already on a statin, or in the first n (say 3) months after they stopped taking a statin. I'm really not sure how to go about rewriting the first CTE to get this information and how to do the comparison after the CTEs are built. I know this is a vague question, but I'm really stumped. Any ideas?
As always, thank you in advance.
Here's the current query:
WITH FAST_MEDS AS
(
select distinct
statins.mrd_pt_id, min(year(statins.order_dts)) as statin_yr
from
edw_dm.patient_medications as statins
inner join mrd.medications as mrd
on statins.mrd_med_id = mrd.mrd_med_id
WHERE mrd.generic_nm in (
'Lovastatin (9664708500)',
'lovastatin-niacin',
'Lovastatin/Niacin',
'Lovastatin',
'Simvastatin (9678583966)',
'ezetimibe-simvastatin',
'niacin-simvastatin',
'ezetimibe/Simvastatin',
'Niacin/Simvastatin',
'Simvastatin',
'Aspirin Buffered-Pravastatin',
'aspirin-pravastatin',
'Aspirin/Pravastatin',
'Pravastatin',
'amlodipine-atorvastatin',
'Amlodipine/atorvastatin',
'atorvastatin',
'fluvastatin',
'rosuvastatin'
)
and YEAR(statins.order_dts) IS NOT NULL
and statins.mrd_pt_id IS NOT NULL
group by statins.mrd_pt_id
)
select *
into #meds
from FAST_MEDS
;
--return patients who had a diagnosis in the list and the year that
--diagnosis was given
with
FAST_DX AS
(
SELECT pd.mrd_pt_id, YEAR(pd.init_noted_dts) as init_yr
FROM edw_dm.patient_diagnoses as pd
inner join mrd.diagnoses as mrd
on pd.mrd_dx_id = mrd.mrd_dx_id
and mrd.icd9_cd in
('728.89','729.1','710.4','728.3','729.0','728.81','781.0','791.3')
)
select *
into #dx
from FAST_DX;
--return patients who had a high cpk value along with the year the cpk
--value was taken
with
FAST_LABS AS
(
SELECT
pl.mrd_pt_id, YEAR(pl.order_dts) as lab_yr
FROM
edw_dm.patient_labs as pl
inner join mrd.labs as mrd
on pl.mrd_lab_id = mrd.mrd_lab_id
and mrd.lab_nm = 'CK (CPK)'
WHERE
pl.lab_val between 1000 AND 999998
)
select *
into #labs
from FAST_LABS;
-- count the number of patients who had a lab value or a medication
-- value taken sometime AFTER their initial statin diagnosis
select
count(distinct p.mrd_pt_id) as ct
from
mrd.patient_demographics as p
join #meds as m
on p.mrd_pt_id = m.mrd_pt_id
AND
(
EXISTS (
SELECT 'A' FROM #labs l WHERE p.mrd_pt_id = l.mrd_pt_id
and l.lab_yr >= m.statin_yr
)
OR
EXISTS(
SELECT 'A' FROM #dx d WHERE p.mrd_pt_id = d.mrd_pt_id
AND d.init_yr >= m.statin_yr
)
)
You probably don't need to select all of your CTE defined queries into temp tables.
I think that the query you're after has the form:
WITH FAST_MEDS(PatientID, StartDate, EndDate) AS
(
--your query for patients on statins, projecting the patient ID and the start/end date for the medication
),
FAST_DX(PatientID, Date) AS
(
--your query for patients with certain diagnosis, projecting the patient ID and the date
),
FAST_LABS(PatientID, Date) AS
(
--your query for patients with certain labs, projecting the patient ID and the date
)
SELECT PatientID
FROM FAST_MEDS
WHERE PatientID IN (SELECT PatientID FROM FAST_DX WHERE Date BETWEEN StartDate AND EndDate OR EndDate IS NULL AND StartDate < Date)
OR PatientID IN (SELECT PatientID FROM FAST_LABS WHERE Date BETWEEN StartDate AND EndDate OR EndDate IS NULL AND StartDate < Date)