Access Query: Subtract last 2 values, specific to ID - sql

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);

Related

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.

Select duplicate by youngest date

I have run into a snag.
A got a DB with employees with multiple startdates.
Employees can start and can get a new contract later.
FE.
ID NAME DATEEMPLOYED FUNCTION
1 Paul 01/01/2016 Director
2 Paul 01/01/2015 Staff Member
3 Jeff 02/05/2016 Director
4 Jeff 01/05/2015 Employee
5 Jeff 01/05/2014 Employee
6 Eric 05/06/2015 Employee
Now I need to get the ID from the latest and the youngest date.
I want to copy the function of the row with the latest date to the oldest date and then delete all but the oldest.
The oldest I can find by:
SELECT * FROM [database].[dbo].[Personel] t WHERE DATEEMPLOYED NOT IN (SELECT MAX(DATEEMPLOYED) AS LastUpdate FROM [database].[dbo].[Personel] GROUP BY Naam,Voornaam)
This returns 10 rows...
Now to find the youngest...
I thought, it would be as easy as changing MAX(DATEEMPLOYED) to MIN(DATEEMPLOYED)...
But I guess not because this only returns 6 rows...
I'm running a live DB so no sample date...
The expected output of the query for the max date per employee is ID 1 and 3 ... The expected output for min date is ID 2 and 5 ...
No number 6
I'am running MS SQL trough an ASP.net application...
The query posted I'm running on the SQL server itself for testing...
Later I'll adapt for the ASP.Net
I want to automatize the deletion of duplicate employees.
Where did i go wrong?
DEPENDING on your version, you could use the window function
Declare #YourTable table (ID int,NAME varchar(50), DATEEMPLOYED Date, [FUNCTION] varchar(50))
Insert Into #YourTable values
(1,'Paul','01/01/2016','Director'),
(2,'Paul','01/01/2015','Staff Member'),
(3,'Jeff','02/05/2016','Director'),
(4,'Jeff','01/05/2015','Employee'),
(5,'Jeff','01/05/2014','Employee'),
(6,'Eric','05/06/2015','Employee')
;with cteBase as (
Select Distinct Name
,Times = count(*) over (Partition By Name)
,MinID = min(ID) over (Partition By Name)
,MaxID = max(ID) over (Partition By Name)
,MinDate = min(DATEEMPLOYED) over (Partition By Name Order By DATEEMPLOYED)
,MaxDate = max(DATEEMPLOYED) over (Partition By Name Order By DATEEMPLOYED Desc)
From #YourTable
)
Select * from cteBase where Times>1
Returns
Name Times MinID MaxID MinDate MaxDate
Jeff 3 3 5 2014-01-05 2016-02-05
Paul 2 1 2 2015-01-01 2016-01-01
Does your DB have NULL dates, if so you need to check for those.

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

Only joining rows where the date is less than the max date in another field

Let's say I have two tables. One table containing employee information and the days that employee was given a promotion:
Emp_ID Promo_Date
1 07/01/2012
1 07/01/2013
2 07/19/2012
2 07/19/2013
3 08/21/2012
3 08/21/2013
And another table with every day employees closed a sale:
Emp_ID Sale_Date
1 06/12/2013
1 06/30/2013
1 07/15/2013
2 06/15/2013
2 06/17/2013
2 08/01/2013
3 07/31/2013
3 09/01/2013
I want to join the two tables so that I only include sales dates that are less than the maximum promotion date. So the result would look something like this
Emp_ID Sale_Date Promo_Date
1 06/12/2013 07/01/2012
1 06/30/2013 07/01/2012
1 06/12/2013 07/01/2013
1 06/30/2013 07/01/2013
And so on for the rest of the Emp_IDs. I tried doing this using a left join, something to the effect of
left join SalesTable on PromoTable.EmpID = SalesTable.EmpID and Sale_Date
< max(Promo_Date) over (partition by Emp_ID)
But apparently I can't use aggregates in joins, and I already know that I can't use them in the where statement either. I don't know how else to proceed with this.
The maximum promotion date is:
select emp_id, max(promo_date)
from promotions
group by emp_id;
There are various ways to get the sales before that date, but here is one way:
select s.*
from sales s
where s.sales_date < (select max(promo_date)
from promotions p
where p.emp_id = s.emp_id
);
Gordon's answer is right on! Alternatively, you could also do a inner join to a subquery to achieve your desired output like this:
SELECT s.emp_id
,s.sales_date
,t.promo_date
FROM sales s
INNER JOIN (
SELECT emp_id
,max(promo_date) AS promo_date
FROM promotions
GROUP BY emp_id
) t ON s.emp_id = t.emp_id
AND s.sales_date < t.promo_date;
SQL Fiddle Demo

SQL: Selecting the lowest value row

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