First row from each group based on condition - sql

Id Date
1 5/11/2015
1 5/11/2015
1 5/12/2015
1 5/13/2015
2 5/11/2015
2 5/11/2015
2 5/12/2015
2 5/13/2015
3 5/14/2015
3 5/15/2015
3 5/16/2015
3 5/17/2015
4 5/13/2015
4 5/13/2015
4 5/14/2015
4 5/15/2015
ID Name
1 Roy
2 Jame
3 Jani
4 Romi
I am not able to get the first row matching with second table
I want to get only one row from each table group by ID where date is greater than today's date (i.e. 5/11/2015), like as shown below.
Id Name Date
1 Roy 5/12/2015
2 Jane 5/12/2015
3 Jani 5/14/2015
4 Romi 5/13/2015

Use cross apply or correlated subquery to do this
select * from Table2 t2
cross apply
(select top 1 [date] from table1 t1
where t2.id = t1.id
AND t1.[date] > convert(date,getdate())
ORDER BY [date] ASC) CS
SQLFIDDLE DEMO

One option would be to use row_number():
with cte as (
select t2.id, t2.name, t1.date,
row_number() over (partition by t2.id order by t1.date) rn
from table1 t1
join table2 t2 on t1.id = t2.id
where t1.date > getdate())
select id, name, date
from cte
where rn = 1
SQL Fiddle Demo

You can use a CTE + ROW_NUMBER:
WITH CTE AS
(
SELECT t1.ID,
t1.[Date],
t2.Name,
RN = ROW_NUMBER() OVER (PARTITION BY t1.ID ORDER BY t1.[Date] ASC)
FROM dbo.Table1 t1
INNER JOIN dbo.Table2 t2
ON t1.ID = t2.ID
WHERE t1.[Date] > GetDate()
)
SELECT ID, Name, Date
FROM CTE
WHERE RN = 1
Demo
The ORDER BY t1.[Date] ASC specifies which row you want to keep, in this case the row with the oldest date, use DESC if you want to keep the newest.

Use a derived table with GROUP BY to get each id's lowest date (after today). Then JOIN with t2:
select t1.id, t2.name, t1.date
from (select Id, min(Date)
from table1
where Date > getdate()
group by id) t1 (id, date) join table 2 t2 on t1.id = t2.id

Related

SQL Server tables with same column but non matching records

I have 2 tables with the same columns, but non matching records.
Table 1
ID
----
1
2
3
4
5
Table 2
ID
-----
NULL
NULL
NULL
NULL
NULL
6
7
8
9
10
I need to pull the NOT NULL records as given below.
Desired output:
ID ID
--------
1 6
2 7
3 8
4 9
5 10
One method is aggregation:
select max(id1) as id1, max(id2) as id2
from ((select t1.id as id1, null as id2 row_number() over (order by id) as seqnum
from t1
) union all
(select null as id1, t2.id row_number() over (order by id) as seqnum
from t2
where t2.id is not null
)
) t
group by seqnum;
Note: This includes all ids, even if one table is larger than the other. If you only want rows where both ids are populated, join is another method:
select t1.id as id1, t2.id as as id2
from (select t1.id as id1, null as id2 row_number() over (order by id) as seqnum
from t1
) t1 join
(select null as id1, t2.id row_number() over (order by id) as seqnum
from t2
where t2.id is not null
) t2
on t1.seqnum = t2.seqnum;

Order by in subquery and alias

I have a problem with a order by in oracle query.
select KEY, B, C, (select D from TABLE1 a where a.KEY = b.KEY and a.DATE<
b.DATE order BY a.DATE and rownum =1 )
FROMSTATUS from TABLE2 b
I known the "order by" is not working in subquery. I modify my query as:
select KEY, B, C, (select * from (select D from TABLE1 a where a.KEY =
b.KEY and a.DATE< b.DATE order by DATE) where rownum = 1)
FROMSTATUS from TABLE2 b
But in this way the B.KEY and B.DATE has not resolved by oracle
I need select only a 1 value from TABLE2 and the value is the first previous a.DATE
Example:
TABLE1
KEY DATE A B C
1 01/31/2000 1 2 3
2 02/25/2000 X Y Z
TABLE2
KEY DATE D
1 01/30/2000 1
1 01/27/2000 2
1 01/25/2000 2
2 02/20/2000 4
2 02/13/2000 1
I need this result:
TABLE1.KEY TABLE1.DATE TABLE1.A TABLE1.B TABLE1.C TABLE2.DATE TABLE2.D
1 01/31/2000 1 2 3 01/30/2000 1
2 02/25/2000 X Y Z 02/20/2000 4
Can you help me?
(i am sorry for my bad english)
row_number() after union will get your output.
select tFinal.DATE, tFinal.KEY
from (select row_number() over (partition by KEY order by t1.T, t1.DATE desc) as rn, t1.DATE, t1.KEY
from
(select DATE, KEY, 't1' as T from TABLE1
union all
select DATE, KEY, 't2' as T from TABLE2) t1) tFinal
Where rn = 2
You can use window functions for this:
WITH cte AS (
SELECT TABLE2.KEY, TABLE2.B, TABLE2.C, TABLE1.D
, ROW_NUMBER() OVER (PARTITION BY TABLE2.KEY, TABLE2.DATE ORDER BY TABLE1.DATE DESC) AS rn
FROM TABLE2
LEFT JOIN TABLE1 ON TABLE2.KEY = TABLE1.KEY AND TABLE2.DATE > TABLE1.DATE
)
SELECT *
FROM cte
WHERE rn = 1
Here's an answer that uses aggregation:
WITH t1 AS (SELECT 1 KEY, to_date('31/01/2000', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 2 KEY, to_date('25/02/2000', 'dd/mm/yyyy') dt FROM dual),
t2 AS (SELECT 1 KEY, to_date('30/01/2000', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 1 KEY, to_date('27/01/2000', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 1 KEY, to_date('25/01/2000', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 2 KEY, to_date('20/02/2000', 'dd/mm/yyyy') dt FROM dual UNION ALL
SELECT 2 KEY, to_date('13/02/2000', 'dd/mm/yyyy') dt FROM dual)
SELECT t1.KEY,
t1.dt t1_date,
MAX(t2.dt) t2_date
FROM t1
LEFT OUTER JOIN t2 ON t1.key = t2.key AND t2.dt < t1.dt
GROUP BY t1.key, t1.dt
ORDER BY t1.key;
KEY T1_DATE T2_DATE
---------- ----------- -----------
1 31/01/2000 30/01/2000
2 25/02/2000 20/02/2000
I'm assuming here that t1.key is a unique column. Whether this is more performant than any of the other answers for your data is up to you to test *{:-)
In Oracle you can use KEEP LAST for this:
select
key,
b,
c,
(
select max(d) keep (dense_rank last order by t2.date)
from table2 t2
where t2.key = t1.key and t2.date < t1.date
) as fromstatus
from table1 t1;
As of Oracle 12c you can also use FETCH FIRST ROW:
select
key,
b,
c,
(
select d
from table2 t2
where t2.key = t1.key and t2.date < t1.date
order by t2.date desc
fetch first row only
) as fromstatus
from table1 t1;
or, moving the subquery to the FROM clause:
select
t1.key,
t1.b,
t1.c,
first_t2.d as fromstatus
from table1 t1
outer apply
(
select d
from table2 t2
where t2.key = t1.key and t2.date < t1.date
order by t2.date desc
fetch first row only
) first_t2;
This last query has the advantage that you could easily select more values from the table2 row than just one.

SQL joining on max ID or dates between date

I have the following tables:
Table 1:
with two columns (PatientID,Name)
Table 2:
with four columns (ID,PatientID,FromDate,ToDate)
I need to join (left join) table1 to table2 (on patientid) to get the values in table2 that has getdate() within Fromdate and todate and if there is no such record, then get the latest id.
I am using SQL 2016.
Table 1 Data:
1 Peter
2 Fady
Table 2 data
1 2019-01-01 2019-02-01
1 2019-03-01 2019-04-01
2 2019-06-01 2019-12-01
2 2020-01-01 2020-01-01
I should get:
1 2019-03-01 2019-04-01
2 2019-06-01 2019-12-01
I think apply does what you want. I think you simply want:
select t1.*, t2.*
from table1 t1 outer apply
(select top (1) t2.*
from table2 t2
where t2.patientid = t.patientid
order by fromdate desc
) t2;
I am guessing that you don't have future fromdates. If you do, then the order by can be tweaked to handle this.
EDIT:
If you can have future dates, then this would be tweaked to:
select t1.*, t2.*
from table1 t1 outer apply
(select top (1) t2.*
from table2 t2
where t2.patientid = t.patientid
order by (case when getdate() >= fromdate and getdate < todate() then 1 else 2 end), id desc
) t2;
You can use a temp table. First get the matching data, then update the missing IDs with the Max(Id) as below:
select table1.*, table2.ID
into #temp
from table1 t1
left outer join table2 t2 on t1.PatientID = t2.PatientID
where getdate() between t2.fromdate and t2.todate
update t
set ID = (select max(ID) from table2 t2 where t.PatientID=t2.PatientID)
from #temp t
where t.ID is null
select * from #temp
You can do a union, only one part will have value:
;WITH cte(ID,PatientID,FromDate,ToDate)
AS
(
SELECT t2.ID,t2.PatientID,t2.FromDate,t2.ToDate
FROM Table1 t1
INNER JOIN Table2 t2
ON t1.PatientID=t2.PatientID
WHERE t2.FromDate >=GETDATE() AND t2.Todate<=GETDATE()
),
cte1 (ID)
AS
(
SELEC TMAX(ID) FROM Table2 WHERE NOT EXISTS(SELECT 1 FROM cte)
)
SELECT ID,PatientID,FromDate,ToDate
FROM cte
UNION ALL
SELECT ID,NULL,NULL,NULL
FROM cte1

Distinct SQL Join two tables

I am trying to join two tables such that I am getting only a first match from the Right table instead of every match in Table2.
So if the query is:
SELECT T1.Name, T2.Dates
FROM Table1 T1
LEFT JOIN Table2 T2 ON T1.ID = T2 = ID
WHERE T1.Name = 'John'
I would like to see
John | 14/11/14
Joe | 10/10/2014
Jane | 25/10/2014
Instead of
John | 14/11/2014
John | 12/10/2014
Joe | 10/10/2014
Jane | 25/10/2014
Jane | 26/10/2014
Which join should I use?
You need to decide which row, you should select. Min or max as commented.
SELECT T1.Name,
( SELECT MIN( T2.Dates) FROM Table2 T2 WHERE T1.ID = T2 = ID) AS Dates
FROM Table1 T1
WHERE T1.Name = 'John'
The ANSI standard function row_number() can be a big help here. It is supported by most databases, so you can do:
SELECT T1.Name, T2.Dates
FROM Table1 T1 LEFT JOIN
(SELECT t2.*, ROW_NUMBER() OVER (PARTITION BY t2.ID ORDER BY t2.DATE DESC) as seqnum
FROM Table2 t2
) T2
ON T1.ID = T2.ID AND seqnum = 1
WHERE T1.Name = 'John';
In your question, you have only one column from the second table, so you can also do this with aggregation:
SELECT t1.ID, t1.Name, MAX(t2.Date)
FROM Table1 T1 LEFT JOIN
Table2 T2
ON t1.ID = t2.ID
WHERE T1.Name = 'John'
GROUP BY t1.ID, t1.Name;
Query
SELECT a.name,
MAX(b.Dates)
FROM tbl1 a
JOIN tbl2 b
ON a.id=b.id
WHERE a.name='John'
GROUP BY a.name;
Demo

sql - how to select multiple columns with only one distinct column from joining multiple tables

I am using SQL Server. I want to select multiple columns with only one distinct column.
For example,
TABLE 1:
ID NAME ...(other columns)
1 A
2 B
3 C
Table 2 (ID and number together is the unique key):
ID Number Year...(other columns)
1 111 2011
2 12345678 2011
2 22222222 2012
3 333 2013
Table 3:
Name Company ...(other columns)
A Amazon
B Google
C Amazon
Each table above has many columns (more than 2). How can get the result so that there are only 5 columns as result without other "useless" columns and the ID column is the distinct column.
More specifically, for example,
The normal sql statement I had is the following:
select distinct ID, NAME, NUMBER, COMPANY, Year
from table1
left join table2 on table1.ID = table2.ID
left join table3 on table1.name = table3.name
group by ID, NAME, NUMBER, COMPANY, year
order by ID desc, Year desc
This will output the following:
ID NAME NUMBER COMPANY YEAR
1 A 111 Amazon 2011
2 B 12345678 google 2011
2 B 22222222 google 2012
3 c 333 Amazon 2013
What I want to have is actually the following:
ID NAME NUMBER COMPANY YEAR
1 A 111 Amazon 2011
2 B 22222222 google 2012
3 c 333 Amazon 2013
I want to have the results without duplicated ID. If there are duplicate ID's, I want to show only the latest one. In above example, ID 2 has 2 rows in table2. I want to show the one with the latest date which is 2012.
How can I achieve this. Thanks in advance.
You can use not exists to only select the latest rows per id (where another row with the same id and a greater year does not exist).
select * from table1 t1
where not exists (
select 1 from table1 t2
where t2.id = t1.id
and t2.year > t1.year
)
using analytic functions (this should be faster than the query above)
select * from
(select *,
row_number() over(partition by id order by year desc) rn
from table1) t1 where rn = 1
edit: applied to your tables
select t2.id, t3.name, t2.number, t3.company, t2.year from
(
select * from
(select *,
row_number() over(partition by id order by year desc) rn
from table2
) t1 where rn = 1
) t2 join table1 t1 on t2.id = t1.id
join table3 t3 on t3.name = t1.name
WITH CTE AS
(
SELECT t1.ID, t1.NAME, t2.NUMBER, t3.COMPANY, t2.Year,
Row_number() OVER(partition BY t1.ID, t1.NAME, t2.NUMBER, t3.COMPANY ORDER BY t2.Year DESC) AS rn
FROM table1 t1
LEFT JOIN table2 t2 ON t1.ID = t2.ID
LEFT JOIN table3 t3 ON t1.name = t3.name
)
SELECT ID, NAME, NUMBER, COMPANY, Year
FROM CTE
WHERE rownum = 1
ORDER BY ID desc, Year desc
I used a subquery, note subqueries are inefficient.
select distinct t1.ID, t1.NAME, t2.NUMBER, t3.COMPANY, t2.Year
from table1 t1
left join table2 t2 on t1.ID = t2.ID
inner join table3 t3 on t1.name = t3.name --inner join to select the latest record only
and t2.Year = (Select MAX(year) from table2 t22
where t22.ID = t2.Id group by ID)
group by t1.ID, t1.NAME, t2.NUMBER, t3.COMPANY, t2.year
order by t1.ID, t2.Year desc
EDIT: using a more efficient CTE
WITH CTE as
(
Select Id, MAX(year) as [yr] from table2 t2 group by ID
)
select distinct t1.ID, t1.NAME, t2.NUMBER, t3.COMPANY, t2.Year
from table1 t1
left join table2 t2 on t1.ID = t2.ID
left join table3 t3 on t1.name = t3.name
inner join CTE on cte.yr = t2.Year
and t2.Id = CTE.Id
group by t1.ID, t1.NAME, t2.NUMBER, t3.COMPANY, t2.year
order by t1.ID, t2.Year desc