Select old records in a table - sql-server

I want the oldest row by date for each Distinct Number. I created this script but the problem is I keep on getting the newest record.
SELECT*
FROM
[Data].[dbo].[IAPT] t1
WHERE
[Last Contact Date] IN
(SELECT MAX([Last Contact Date])
FROM [Data].[dbo].[IAPT]
WHERE t1.[Number] = [Data].[dbo].[IAPT].[Number]
AND
[Last Contact Date] NOT IN
(SELECT MAX([Last Contact Date])
FROM [Data].[dbo].[IAPT]
WHERE t1.[Pseudo] = [Data].[dbo].[IAPT].[Pseudo]))
The Table:
Pseudo Number Last Contact Date
0X1 18 17/06/2013
0X1 18 16/04/2013
0X2 19 25/04/2013
0X2 19 16/07/2013
Desired Result:
Number Last Contact Date
1 16/04/2013
2 25/04/2013
Any help would be appreciated. Thank You

You should use MIN function instead of MAX function
SELECT*
FROM
[Data].[dbo].[IAPT] t1
WHERE
[Last Contact Date] IN
(SELECT MIN([Last Contact Date])
FROM [Data].[dbo].[IAPT]
WHERE t1.[Number] = [Data].[dbo].[IAPT].[Number]
AND
[Last Contact Date] NOT IN
(SELECT MIN([Last Contact Date])
FROM [Data].[dbo].[IAPT]
WHERE t1.[Pseudo] = [Data].[dbo].[IAPT].[Pseudo]))

You can use ROW_NUMBER with a PARTITION BY clause:
SELECT Pseudo, Number, [Last Contact Date]
FROM (
SELECT Pseudo, Number, [Last Contact Date],
ROW_NUMBER() OVER (PARTITION BY Number
ORDER BY [Last Contact Date]) AS rn
FROM [Data].[dbo].[IAPT]) AS t
WHERE t.rn = 1
The first record within each Number partition is the one having the oldest date.

This way simple
SELECT PSEUDO, NUMBER , MIN ([LAST CONTACT DATE]) FROM [DATA].[DBO].[IAPT] T1
GROUP BY PSEUDO, NUMBER

Related

Join two tables by MRN and date1 >= MAX(date2)

I need to combine the data from two hospital activity reports. What happens is this: Patients get admitted to a spinal department. Some of whom then get referred to put on ventilation. After a while patient is discharged. Later, the same patient may or may not get re-referred back to the spinal department and may or may not be re-referred for ventilation. I am sent activity data in two reports:
Monthly Activity Report:
[MRN] [NHS Number] [Admission Date] [DoB] [Blah] [Blah]
Ventilation Report
[MRN] [Admission Date] [Ventilation Days] [Ventilation Type] [blah] [blah]
N.B. The Admission Date on the Ventilation Report is the date they are referred for ventilation. This may be the same day, or some date after they are referred into spinal dept.
What I need to achieve is this: join each row to the most immediate entry prior to the patient being referred to ventilation. I need to avoid duplicating rows, but I cannot join it to the most recent row in the Monthly Activity Report as this could easily be a subsequent referral and the other information will not be applicable.
By following the answer to a similar question on Stackoverflow, I came up with this code:
SELECT [Year], [Month], MRN, [NHS Number], [Admission Date] AS [VD
Admission Date],
[Admit date] AS [MAR Admit Date], Days,
[Ventilation Type], [Ventilation Route], [Ventilation Time], [Package of
care class],
[Para/Tetra/No deficit], [Social charge date commenced ] AS [Social charge
date], [Discharge date]
FROM Spinal_Costing.Vented_Days VD
LEFT JOIN (SELECT *, ROW_NUMBER() OVER(PARTITION BY [Patient MRN] ORDER BY
[Admit Date] DESC) AS row
FROM Spinal_Costing.MAR
) MAR ON VD.MRN = MAR.[Patient MRN]
WHERE MAR.row = 1;
But this returns the most recent entry in MAR for each patient.
This can also be achieved with an apply that references the values in Vented_Days and simply returns a top 1 for each row. cross apply won't return null values whereas outer apply will:
declare #vd table(MRN int,AdmissionDate date);
declare #mar table(MRN int,AdmissionDate date);
insert into #vd values
(1,'20190102')
,(1,'20190106')
,(2,'20190104')
,(3,'20190101');
insert into #mar values
(1,'20190101')
,(1,'20190105')
,(2,'20190102');
select v.MRN
,v.AdmissionDate
,m.AdmissionDate
from #vd as v
outer apply (select top 1 m.AdmissionDate
from #mar as m
where v.MRN = m.MRN
and v.AdmissionDate >= m.AdmissionDate
order by m.AdmissionDate desc
) as m
order by v.MRN
,v.AdmissionDate;
Output
+-----+---------------+---------------+
| MRN | AdmissionDate | AdmissionDate |
+-----+---------------+---------------+
| 1 | 2019-01-02 | 2019-01-01 |
| 1 | 2019-01-06 | 2019-01-05 |
| 2 | 2019-01-04 | 2019-01-02 |
| 3 | 2019-01-01 | NULL |
+-----+---------------+---------------+
You were on the right track, you just need to add a JOIN to that derived table to limit the rows in the Spinal_Costing.MAR table to those that came at, or before discharge.
SELECT
[Year],
[Month],
MRN,
[NHS Number],
[Admission Date] AS [VD Admission Date],
[Admit date] AS [MAR Admit Date],
Days,
[Ventilation Type],
[Ventilation Route],
[Ventilation Time],
[Package of care class],
[Para/Tetra/No deficit],
[Social charge date commenced ] AS [Social charge date],
[Discharge date]
FROM
Spinal_Costing.Vented_Days VD
LEFT JOIN
(SELECT
*,
ROW_NUMBER() OVER(PARTITION BY [Patient MRN] ORDER BY [Admit Date] DESC) AS row
FROM Spinal_Costing.MAR
--added the JOIN and WHERE clause here
INNER JOIN Spinal_Costing.Vented_Days
ON Spinal_Costing.Vented_Days.MRN = Spinal_Costing.MAR.[Patient MRN]
WHERE Spinal_Costing.MAR.[Admit Date] <= Spinal_Costing.Vented_Days.[Discharge date]
) MAR ON VD.MRN = MAR.[Patient MRN]
WHERE MAR.row = 1;

selecting some data within specific number of days

I'm working on sales data and I want to know if Customer A purchased product X from more than one provider within 3 days and I'm working on only one date Claim Date
I Can't find T-Sql query for it
for example
SELECT CusName,ProdName,ProvName
FROM table1
WHERE [Claim Date] between Day([Claim Date]) and DATEADD (Day ,-3 , [Claim Date]
A WHERE EXISTS clause should do the job:
SELECT CusName,ProdName,ProvName
FROM table1 a
WHERE EXISTS (
SELECT 1 FROM table1 b WHERE
b.CusName=a.CusName AND
b.ProdName=a.ProdName AND
b.ProvName!=a.ProvName AND
ABS(DATEDIFF(day,a.ClaimDate,b.ClaimDate))<3
)
You can use the below code for that
SELECT t1.CusName,t1.ProdName,t1.ProvName,t2.ProvName
FROM table t1
JOIN table t2 ON t1.CusName=t2.CusName AND t1.ProdName=t2.ProdName
WHERE t1.ProvName!=t2.ProvName
AND ABS(DATEDIFF(day,t1.ClaimDate,t2.ClaimDate)) = 3
You need a having clause and a count of the providers:
SELECT CusName, COUNT(DISTINCT ProvName) Provider_count
FROM table1
WHERE [Claim Date] between Day([Claim Date]) and DATEADD (Day ,-3 , [Claim Date]
AND ProdName = 'X'
GROUP BY CusName
HAVING COUNT(DISTINCT ProvName) > 1
Note, you do not need to include the count in the select clause, but you do need it in the having clause.

SQL Query for Comparing Customer Order Amounts

I'm looking to create a query that could compare a customer's latest order purchase amount to the previous order amount of the customer's last purchase. Please see example data screenshot below:
Ideally I'd like the query to look for these things in the results:
Total amount from previous order before most recent order date (in this case 9/6/18 would be most recent and 2/2/17 would be the last purchase)
Difference in amount between most recent order and last order amount ($2000-$25 = $1975)
Create a condition in the query to look for customers whose most recent order attempt is 1000 > last purchase amount and the age of the customer's account age is > than 60 days
Note: These conditions for the last bullet could be modified as needed (customer's account age is > 90 days, different in order amount is $500, etc)
Thank you for the assistance!
For 2012 onward you can use LAG
declare #amount decimal(16,2) = 1000
declare #days int = 60
select
*
,TotalWithPrevious = [Order Amount] + lag([Order Amount]) over (partition by UserID order by [Order Date] desc)
,DifferenceofPrevious = [Order Amount] - lag([Order Amount]) over (partition by UserID order by [Order Date] desc)
,CheckCondition = case
when [Order Amount] - lag([Order Amount]) over (partition by UserID order by [Order Date] desc) >= #amount
and datediff(day,[Order Date],lag([Order Date]) over (partition by UserID order by [Order Date] desc)) >= #days
then 'True'
else 'False'
end
from YourTable

Combine two columns in the ON search condition of MERGE sql server

I have two date columns, [START DATE] and [END DATE] and I want to only insert records whose dates are not in either column. This is what my logic looks like:
MERGE TABLE_A AS Target
USING (SELECT DISTINCT * FROM TABLE_B) AS Source
ON (SELECT Target.[START DATE] + Target.[END DATE] AS Target.[COMPAREDATE] =
SELECT Source.[START DATE] + Source.[END DATE] AS Source.[COMPAREDATE])
WHEN NOT MATCHED THEN
INSERT ([DATE ADDED], [END DATE], ...)
VALUES (Source.[DATE ADDED], Source.[END DATE], ...);
I also tried the code below but it continued to insert duplicates:
ON (Target.[START DATE] = Source.[START DATE] AND Target.[END DATE] = Source.[END DATE])
I'd appreciate any help, thanks!
This code was close:
ON (Target.[START DATE] = Source.[START DATE] AND Target.[END DATE] = Source.[END DATE])
It says, "if both the StartDate and the EndDate match, then the rows match".
If you want to say "if either the StartDates match or the EndDates match, then the rows match", then you should do this:
ON (Target.[START DATE] = Source.[START DATE] OR Target.[END DATE] = Source.[END DATE])

T-SQL return top one field based on count of another

I've struggled to search successfully for this as I haven't figured out a search string describing what I want to do, apologies if this has been covered already.
I have a table that contains among others a contract number, a start date, a serial number and a datestamp. These are Many:Many.
What I'm trying to achieve is to return the start date for each individual contract number with the largest number of unique serial numbers and the most recent datestamp, where that start date is valid.
This, as I guess is obvious to T-SQL experts only returns me the one contract number with the largest number of serials. Can anyone tell me what I'm doing wrong?
SELECT TOP (1)
[Contract ID], [Item Begin Date] AS Start_Date,
COUNT([Serial Number]) AS CountSerials,
Datestamp
FROM
SourceTable
GROUP BY
[Contract ID], [Item Begin Date], Datestamp
HAVING
([Item Begin Date] > CONVERT(DATETIME, '1900-01-01 00:00:00', 102))
ORDER BY
CountSerials DESC, Datestamp DESC
Cheers,
Alex
You can put that into a temporary table (without using TOP (1) or oder by):
I changed some table names in the process,
if exists (select * from tempdb.sys.tables where name = '##tmp')
drop table ##tmp
SELECT * into ##tmp
from
(
select
[Contract_ID], [Begin_Date] AS Start_Date,
COUNT([serials]) AS CountSerials,
Datestamp
FROM
SourceTable
GROUP BY
[Contract_ID], [begin_date], Datestamp
HAVING
[begin_date] > CONVERT(DATETIME, '1900-01-01 00:00:00', 102)
) a
select contract_id,Start_Date, max(countserials) as MAXCOUNT, Datestamp from ##tmp
group by contract_id,Start_Date,Datestamp
you can do a subquery with aggregation and extract your desired results from it:
SELECT distinct *
FROM
(
Select
[Contract ID], [Item Begin Date] AS Start_Date,
COUNT([Serial Number]) OVER(PARTITION BY [Contract ID]) AS CountSerials,
datestamp,
MAX(Datestamp) OVER (PARTITION BY [Contract ID]) maxdatestamp
FROM
SourceTable
WHERE
([Item Begin Date] > CONVERT(DATETIME, '1900-01-01 00:00:00', 102))
) x
WHERE
datestamp=maxdatestamp

Resources