Suppress Nonadjacent Duplicates in Report - sql

Medical records in my Crystal Report are sorted in this order:
...
Group 1: Score [Level of Risk]
Group 2: Patient Name
...
Because patients are sorted by Score before Name, the report pulls in multiple entries per patient with varying scores - and since duplicate entries are not always adjacent, I can't use Previous or Next to suppress them. To fix this, I'd like to only display the latest entry for each patient based on the Assessment Date field - while maintaining the above order.
I'm convinced this behavior can be implemented with a custom SQL command to only pull in the latest entry per patient, but have had no success creating that behavior myself. How can I accomplish this compound sort?
Current SQL Statement in use:
SELECT "EpisodeSummary"."PatientID",
"EpisodeSummary"."Patient_Name",
"EpisodeSummary"."Program_Value"
"RiskRating"."Rating_Period",
"RiskRating"."Assessment_Date",
"RiskRating"."Episode_Number",
"RiskRating"."PatientID",
"Facility"."Provider_Name",
FROM (
"SYSTEM"."EpisodeSummary"
"EpisodeSummary"
LEFT OUTER JOIN "FOOBARSYSTEM"."RiskAssessment" "RiskRating"
ON (
("EpisodeSummary"."Episode_Number"="RiskRating"."Episode_Number")
AND
("EpisodeSummary"."FacilityID"="RiskRating"."FacilityID")
)
AND
("EpisodeSummary"."PatientID"="RiskRating"."PatientID")
), "SYSTEM"."Facility" "Facility"
WHERE (
"EpisodeSummary"."FacilityID"="Facility"."FacilityID"
)
AND "RiskRating"."PatientID" IS NOT NULL
ORDER BY "EpisodeSummary"."Program_Value"

The SQL code below may not be exactly correct, depending on the structure of your tables. The code below assumes the 'duplicate risk scores' were coming from the RiskAssessment table. If this is not correct, the code may need to be altered.
Essentially, we create a derived table and create a row_number for each record, based on the patientID and ordered by the assessment date - The most recent date will have the lowest number (1). Then, on the join, we restrict the resultset to only select record #1 (each patient has its own rank #1).
If this doesn't work, let me know and provide some table details -- Should the Facility table be the starting point? are there multiple entries in EpisodeSummary per patient? thanks!
SELECT es.PatientID
,es.Patient_Name
,es.Program_Value
,rrd.Rating_Period
,rrd.Assessment_Date
,rrd.Episode_Number
,rrd.PatientID
,f.Provider_Name
FROM SYSTEM.EpisodeSummary es
LEFT JOIN (
--Derived Table retreiving highest risk score for each patient)
SELECT PatientID
,Assessment_Date
,Episode_Number
,FacilityID
,Rating_Period
,ROW_NUMBER() OVER (
PARTITION BY PatientID ORDER BY Assessment_Date DESC
) AS RN -- This code generates a row number for each record. The count is restarted for every patientID and the count starts at the most recent date.
FROM RiskAssessment
) rrd
ON es.patientID = rrd.patientid
AND es.episode_number = rrd.episode_number
AND es.facilityid = rrd.facilityid
AND rrd.RN = 1 --This only retrieves one record per patient (the most recent date) from the riskassessment table
INNER JOIN SYSTEM.Facility f
ON es.facilityid = f.facilityid
WHERE rrd.PatientID IS NOT NULL
ORDER BY es.Program_Value

Related

SQL - Min() on a Daily Query

I am trying to pull some specific information from an access control database.
I have a query providing results spanning several days. For a specific day, I need to get the first record of each person for that specific day. I have totally muddled the entire bit, hence my questions
This is the code used to pull the initial query
Select
Message.TimeStamp_SPM,
Message.FirstName,
Message.LastName,
Message.CardNumber,
Message.MessageDescription,
Message.Description,
Department.Description As Description1
From
Message Inner Join
CardHolder On CardHolder.CardHolderID = Message.CardHolderID Inner Join
Department On CardHolder.DepartmentID = Department.DepartmentID
Where
Message.TimeStamp_SPM > Convert(datetime,'2021-03-02',120) And
Message.TimeStamp_SPM < Convert(datetime,'2021-03-03',120) And
Message.Description Not Like '%Truck%'
From this query I need to display the obtain the first record of each person for that specific date. Any advice on the most efficient way to obtain the desired result?
From this query I need to display the obtain the first record of each person for that specific date.
Assuming "person" is CardHolderId, then include that in your query. You can then use window functions to get the most recent record for each CardHolderId:
with cte as (
<your query here with CardHolderId>
)
select cte.*
from (select cte.*,
row_number() over (partition by CardHolderID order by TimeStamp_SPM desc) as seqnum
from cte
) cte
where seqnum = 1;

Why do I get extra rows in LEFT JOIN when joining to an ID and TIMESTAMP column?

I have a table that contains multiple registration periods (date and time for the start of the registration, as well as date and time for when that instance of registration ends). For each row (registration period), there is a status column that contains the status at the end of the registration period. I was trying to get the status associated with the most recent end date of registration per a given ID. I've used a window function to get the most recent end date of interest per ID, and then I wanted to LEFT JOIN on ID and end date to get the status from the same table on which I used the window function. There should really just be one just one combination for a given end date and status per ID, but somehow I get more rows that what's in the left table.
Like I mentioned earlier, my approach was to use a window function to get MAX(end_date) per ID and some other column, let's call it enrollment_number. Then use LEFT JOIN on this table and its parent table to bring in status associated with that date only. Later, I'd like to use the result of this join to bring in the status associated with the end date into other tables where I need it.
WITH
my_first_test AS
(
SELECT my_id,
enrollment_number,
MAX(end_date_of_enrollment) OVER (partition by my_id, enrollment_number) AS end_date_enrolled
FROM enrollments
)
SELECT mft.my_id, mft.end_date_enrolled, e.status
FROM my_first_test AS mft
LEFT JOIN enrollments AS e
ON mft.my_id = e.my_id AND mft.end_date_enrolled = e.end_date_enrolled;
The CTE returns 42917 rows, same number of rows as in the enrollments table, which it should be if I understand it correctly.
Then, I LEFT JOIN enrollments, to bring in information from the status column also contained in the enrollments table. The LEFT JOIN is done on my_id and end_date_enrolled.
I expect 42917 rows in the resulting table, because my_id and end_date_enrolled together should be unique. However, I get slightly more rows in my final table - 44408. I was wondering if the StackOverflow community would be able to help me solve this mystery. I am using SQL in AWS Redshift.
You have duplicates in enrollments. You can find them with aggregation:
SELECT my_id, end_date_enrolled, COUNT(*)
FROM enrollments AS e
GROUP BY my_id, end_date_enrolled
HAVING COUNT(*) > 1;

SQL to find best row in group based on multiple columns?

Let's say I have an Oracle table with measurements in different categories:
CREATE TABLE measurements (
category CHAR(8),
value NUMBER,
error NUMBER,
created DATE
)
Now I want to find the "best" row in each category, where "best" is defined like this:
It has the lowest errror.
If there are multiple measurements with the same error, the one that was created most recently is the considered to be the best.
This is a variation of the greatest N per group problem, but including two columns instead of one. How can I express this in SQL?
Use ROW_NUMBER:
WITH cte AS (
SELECT m.*, ROW_NUMBER() OVER (PARTITION BY category ORDER BY error, created DESC) rn
FROM measurements m
)
SELECT category, value, error, created
FROM cte
WHERE rn = 1;
For a brief explanation, the PARTITION BY clause instructs the DB to generate a separate row number for each group of records in the same category. The ORDER BY clause places those records with the smallest error first. Should two or more records in the same category be tied with the lowest error, then the next sorting level would place the record with the most recent creation date first.

Return All Historical Account Records for Accounts with Change in Corresponding Value

I am trying to select all records in a time-variant Account table for each account with a change in an associated value (e.g. the maturity date). A change in the value will result in the most recent record for an account being end-dated and a new record (containing a new effective date of the following day) being created. The most recent records for accounts in this table have an end-date of 12/31/9000.
For instance, in the below illustration, account 44444444 would not be included in my query result set since it hasn't had a change in the value (and thus also has no additional records aside from the original); however, the other accounts have multiple changes in values (and multiple records), so I would want to see those returned.
I've tried using the row_num function, as well as a reflexive join, but for some reason I'm not getting the expected results. What are some ways to obtain the results I need?
Note: The primary key for this table includes the acct_id and eff_dt. Also, I'm using PostgreSQL in a Greenplum environment.
Here are two types of queries I tried to use but which produced problematic results:
Query 1
Query 2
If you want only the accounts, use aggregation:
select acct_id
from t
group by acct_id
having min(value) <> max(value);
Based on your description, you could also use count(*) >.
If you want the original records, you can use window functions:
select t.*
from (select t.*, count(*) over (partition by acct_id) as cnt
from t
) t
where cnt > 1;

OracleSQL: Assigning employees to groups with date values, querying current assignments by date

I have a database which consists of employees (one table) which can be assigned to groups (another table). Bother are joined together with another table, employee-to-group, which lists the group id, the employee id and the start date of the assignment.
An employee always has to be assigned to a group, but the assignments can change daily. One employee could be working in group A for day, then change into group B and work in group C only a week later.
My task is to find out which employees are assigned to a certain group given by its name at any given date. So the input should be: group name, date and I want the output to be the data of all the employees which are part of that group at the given moment in time.
Here's an SQL fiddle with some test data:
http://sqlfiddle.com/#!9/6d0bb
I recreated the database with mysql-statements because I couldn't figure out the oracle statements, I'm sorry.
As you can see from the test data, some employees may never change groups, while others change frequently. THere are also employees which are planned to change assignments in the future. The query has to account for that.
Because the application is a legacy one, the values (especially in the date field) are questionable. They are given as "days since the 1st of january, 1990", so the entry "9131" means "1st of january, 2015". 9468 would be today (2015-12-04) and 9496 would be 2016-01-01).
What I already have is code to find out the "date value" for any given date in what I call the "legacy format" of the application I'm working with (here I've just used CURRENT_DATE):
SELECT FLOOR(CURRENT_DATE - TO_DATE('1990-01-01', 'YYYY-MM-DD')) AS diffdate
For finding out which group a certain employee is assigned to, I tried:
SELECT * FROM history h
WHERE emp_nr = 1 AND valid_from <= 9131
ORDER BY valid_from DESC
FETCH FIRST ROW ONLY;
which should return me the group which an employee is assigned to on the 1st of january 2015.
What I do need help with is creating a statement that joins all tables does the same for a whole group instead of only one employee (as there are thousands of employees in the database and I only want the data of at most 10 groups).
I'm thankful for any kind of pointers in the right direction.
Use row_number to rank your history and get the latest group, just as you did with your FETCH FIRST query:
select *
from
(
select
h.*,
row_number() over (partition by emp_nr order by valid_from desc) as rn
from history h
where valid_from <= 9131
)
where rn = 1
You can then join this result with other tables.