SQL: Selecting the lowest value row - sql

I am looking at hospital claims data and there are multiple rows with the same admission date. I only want one admission date per patient. If there are multiple rows with the same admission date, I want to select the row with the largest LOS, or when LOS are equal, I want to select the one with the oldest admission date. For example, given the following data:
ID ADMIT DC LOS CLMID
-- ----- -- --- -----
1 1-1-07 1-1-07 0 XXX
1 1-2-07 1-2-07 0 XXX
2 1-5-07 1-10-07 5 YYY
3 2-8-07 2-8-07 0 ZZZ
3 2-8-07 2-12-07 4 ZZZ
3 2-8-07 2-10-07 2 ZZZ
I would want to select:
ID ADMIT DC LOS CLMID
-- ----- -- --- -----
1 1-1-07 1-1-07 0 XXX
2 1-5-07 1-10-07 5 YYY
3 2-8-07 2-12-07 4 ZZZ
I've tried using the MIN aggregrate function, but I'm pretty lost on how to get where I want. I'm new to SQL and would appreciate any help!
So far, this is my best shot:
SELECT DISTINCT
ID, ADMIT, DC, LOS, CLMID, MIN(ADMIT)
FROM
TABLE1
GROUP BY
ID, ADMIT, DC, LOS, CLMID
ORDER BY
ID
I've also tried just selecting just the maximum LOS instead of the minimum admit, but that doesn't do it either.
Thanks :)

This is a prioritization and you can solve these problems with row_number():
select t.*
from (select t.*,
row_number() over (partition by id order by admit asc, los desc) as seqnum
from table1 t
) t
where seqnum = 1;
A couple of notes:
I assume that dates are actually stored as date/times in the database and not as strings.
The conditions in your first paragraph are a bit vague. This gets the one row for each patient withi the highest los on the earliest admit date.

In MySQL this would be something like:
Select distinct ID, * from admissions_table order by DC DESC group by ID

Related

Access Query: Subtract last 2 values, specific to ID

Help appreciated! My table is setup as follows:
fake data TableName = GAD7
[PatientID Date Value
Sam 10/21/2022 15
George 06/12/2022 7
Luke 09/03/2021 11
Sam 05/15/2020 20
George 12/02/2017 2
George 01/01/1992 6][1]
So I have potentially multiple rows of the same patient, w/different dates.
I need to create a query that subtracts the LAST 2/most recent values for each patient.
So my query would show only those with 2+ records. Negative values are fine/expected.
My successful query would then show:
PatientID (LastScore - 2nd_toLastScore)
Sam -5.0
George 5.0
Luke is not shown because he only has one value
I was able to formulate a query to show only those PatientIDs with >= 2 records and last date and last value. I am not sure how to get the second from last date/value AND THEN subtract those values.
Access query
The SQL view :
SELECT GAD7.PatientID, Count(GAD7.PatientID) AS CountOfPatientID, Last(GAD7.TestDate) AS LastDate, Last(GAD7.Score) AS LastScore
FROM GAD7
GROUP BY GAD7.PatientID
HAVING (((Count(GAD7.PatientID))>=2))
ORDER BY GAD7.PatientID;
Consider:
Query1: Score1
SELECT GAD7.*
FROM GAD7
WHERE 1=(SELECT Count(*)+1 FROM GAD7 AS G7
WHERE G7.PatientID=GAD7.PatientID AND G7.TestDate>GAD7.TestDate);
Query2: Score2
SELECT GAD7.*
FROM GAD7
WHERE 2=(SELECT Count(*)+1 FROM GAD7 AS G7
WHERE G7.PatientID=GAD7.PatientID AND G7.TestDate>GAD7.TestDate);
Query3:
SELECT Score2.PatientID, [Score2].[Score]-[Score1].[Score] AS D
FROM Score1 INNER JOIN Score2 ON Score1.PatientID = Score2.PatientID;
Could nest the SQL statements for an all-in-one query.
Or this all-in-one version using TOP N to pull previous Score:
SELECT GAD7.*, (SELECT TOP 1 Score FROM GAD7 AS Dupe
WHERE Dupe.PatientID = GAD7.PatientID AND Dupe.TestDate<GAD7.TestDate
ORDER BY Dupe.TestDate DESC) AS PrevScore
FROM GAD7 WHERE PatientID IN
(SELECT PatientID FROM GAD7 GROUP BY PatientID HAVING Count(*)>1)
AND 1=(SELECT Count(*)+1 FROM GAD7 AS G7 WHERE G7.PatientID=GAD7.PatientID AND G7.TestDate>GAD7.TestDate);

How to consecutively count everything greater than or equal to itself in SQL?

Let's say if I have a table that contains Equipment IDs of equipments for each Equipment Type and Equipment Age, how can I do a Count Distinct of Equipment IDs that have at least that Equipment Age.
For example, let's say this is all the data we have:
equipment_type
equipment_id
equipment_age
Screwdriver
A123
1
Screwdriver
A234
2
Screwdriver
A345
2
Screwdriver
A456
2
Screwdriver
A567
3
I would like the output to be:
equipment_type
equipment_age
count_of_equipment_at_least_this_age
Screwdriver
1
5
Screwdriver
2
4
Screwdriver
3
1
Reason is there are 5 screwdrivers that are at least 1 day old, 4 screwdrivers at least 2 days old and only 1 screwdriver at least 3 days old.
So far I was only able to do count of equipments that falls within each equipment_age (like this query shown below), but not "at least that equipment_age".
SELECT
equipment_type,
equipment_age,
COUNT(DISTINCT equipment_id) as count_of_equipments
FROM equipment_table
GROUP BY 1, 2
Consider below join-less solution
select distinct
equipment_type,
equipment_age,
count(*) over equipment_at_least_this_age as count_of_equipment_at_least_this_age
from equipment_table
window equipment_at_least_this_age as (
partition by equipment_type
order by equipment_age
range between current row and unbounded following
)
if applied to sample data in your question - output is
Use a self join approach:
SELECT
e1.equipment_type,
e1.equipment_age,
COUNT(*) AS count_of_equipments
FROM equipment_table e1
INNER JOIN equipment_table e2
ON e2.equipment_type = e1.equipment_type AND
e2.equipment_age >= e1.equipment_age
GROUP BY 1, 2
ORDER BY 1, 2;
GROUP BY restricts the scope of COUNT to the rows in the group, i.e. it will not let you reach other rows (rows with equipment_age greater than that of the current group). So you need a subquery or windowing functions to get those. One way:
SELECT
equipment_type,
equipment_age,
(Select COUNT(*)
from equipment_table cnt
where cnt.equipment_type = a.equipment_type
AND cnt.equipment_age >= a.equipment_age
) as count_of_equipments
FROM equipment_table a
GROUP BY 1, 2, 3
I am not sure if your environment supports this syntax, though. If not, let us know we will find another way.

SQL Compare Rows With Duplicate IDs and Return One With Lowest Sequence Number

Reaching out for help. I've seen plenty of answers on how to use DUPLICATE, but not quite how I need it. Let's say I have the result of query that looks like the following.
query result
Incident_No Open_Approval_Step Approval_ID
------------- -------------------- -------------------
1 3 Tech
1 4 Cust_Serv
2 1 Incident_Recorder
2 2 Estimation
2 3 Tech
3 4 Cust_Serv
3 5 Mgmt
3 6 Closure
And I need one row for each incident number with the smallest numbered approval step. So the result should look like this.
filtered query result
Incident_No Open_Approval_Step Approval_ID
------------- -------------------- -------------------
1 3 Tech
2 1 Incident_Recorder
3 4 Cust_Serv
Edit This is what I came up with in the end
SELECT DISTINCT
MIN(OPEN_APPROVAL_STEP) OVER(PARTITION BY INCIDENT_NO ORDER BY OPEN_APPROVAL_STEP ASC) AS CUR_APP_STEP,
INCIDENT_NO
FROM T
You can use row_number():
select *
from (
select
t.*,
row_number() over(partition by incident_no order by open_approval_step) rn
from mytable t
) t
where rn = 1
With just one extra column appart from the incident number and approval step, another option is aggregation and Oracle's keep syntax:
select
incident_no,
min(open_approval_step) open_approval_step,
min(approval_id) keep(dense_rank first order by open_approval_step) approval_id
from mytable
group by incident_no
If you have just three columns, you can easily use aggregation:
select incident_no, min(open_approval_step),
min(approval_id) keep (dense_rank first order by open_approval_step)
from t
group by incident_no;

Complex SQL query or queries

I looked at other examples, but I don't know enough about SQL to adapt it to my needs. I have a table that looks like this:
ID Month NAME COUNT First LAST TOTAL
------------------------------------------------------
1 JAN2013 fred 4
2 MAR2013 fred 5
3 APR2014 fred 1
4 JAN2013 Tom 6
5 MAR2014 Tom 1
6 APR2014 Tom 1
This could be in separate queries, but I need 'First' to equal the first month that a particular name is used, so every row with fred would have JAN2013 in the first field for example. I need the 'Last" column to equal the month of the last record of each name, and finally I need the 'total' column to be the sum of all the counts for each name, so in each row that had fred the total would be 10 in this sample data. This is over my head. Can one of you assist?
This is crude but should do the trick. I renamed your fields a bit because you are using a bunch of "RESERVED" sql words and that is bad form.
;WITH cte as
(
Select
[NAME]
,[nmCOUNT]
,ROW_NUMBER() over (partition by NAME order by txtMONTH ASC) as 'FirstMonth'
,ROW_NUMBER() over (partition by NAME order by txtMONTH DESC) as 'LastMonth'
,SUM([nmCOUNT]) as 'TotNameCount'
From Table
Group by NAME, [nmCOUNT]
)
,cteFirst as
(
Select
NAME
,[nmCOUNT]
,[TotNameCount]
,[txtMONTH] as 'ansFirst'
From cte
Where FirstMonth = 1
)
,cteLast as
(
Select
NAME
,[txtMONTH] as 'ansLast'
From cte
Where LastMonth = 1
Select c.NAME, c.nmCount, c.ansFirst, l.ansLast, c.TotNameCount
From cteFirst c
LEFT JOIN cteLast l on c.NAME = l.NAME

SQL: How to get the AVG(MIN(number))?

I am looking for the AVERAGE (overall) of the MINIMUM number (grouped by person).
My table looks like this:
Rank Name
1 Amy
2 Amy
3 Amy
2 Bart
1 Charlie
2 David
5 David
1 Ed
2 Frank
4 Frank
5 Frank
I want to know the AVERAGE of the lowest scores. For these people, the lowest scores are:
Rank Name
1 Amy
2 Bart
1 Charlie
2 David
1 Ed
2 Frank
Giving me a final answer of 1.5 - because three people have a MIN(Rank) of 1 and the other three have a MIN(Rank) of 2. That's what I'm looking for - a single number.
My real data has a couple hundred rows, so it's not terribly big. But I can't figure out how to do this in a single, simple statement. Thank you for any help.
Try this:
;WITH MinScores
AS
(
SELECT
"Rank",
Name,
ROW_NUMBER() OVER(PARTITION BY Name ORDER BY "Rank") row_num
FROM Table1
)
SELECT
CAST(SUM("Rank") AS DECIMAL(10, 2)) /
COUNT("Rank")
FROM MinScores
WHERE row_num = 1;
SQL Fiddle Demo
Selecting the set of minimum values is straightforward. The cast() is necessary to avoid integer division later. You could also avoid integer division by casting to float instead of decimal. (But you should be aware that floats are "useful approximations".)
select name, cast(min(rank) as decimal) as min_rank
from Table1
group by name
Now you can use the minimums as a common table expression, and select from it.
with minimums as (
select name, cast(min(rank) as decimal) as min_rank
from Table1
group by name
)
select avg(min_rank) avg_min_rank
from minimums
If you happen to need to do the same thing on a platform that doesn't support common table expressions, you can a) create a view of minimums, and select from that view, or b) use the minimums as a derived table.
You might try using a derived table to get the minimums, then get the average minimum in the outer query, as in:
-- Get the avg min rank as a decimal
select avg(MinRank * 1.0) as AvgRank
from (
-- Get everyone's min rank
select min([Rank]) as MinRank
from MyTable
group by Name
) as a
I think the easiest one will be
for max
select name , max_rank = max(rank)
from table
group by name;
for average
select name , avg_rank = avg(rank)
from table
cgroup by name;