How to get rows from two tables on maximum value of particular field - sql

I have two tables that has date_updated column.
TableA is like below
con_id date_updated type
--------------------------------------------
123 19/06/2018 2
123 15/06/2018 1
123 01/05/2018 3
101 06/04/2018 1
101 05/03/2018 2
And I have TableB that also has the same structure
con_id date_updated type
--------------------------------------------
123 15/05/2018 2
123 01/05/2018 1
101 07/06/2018 1
The resultant table should have the data with the recent date
con_id date_updated type
--------------------------------------------
123 19/06/2018 2
101 07/06/2018 1
Here the date_updated column is datetime datatype of sql server. I tried this by using group by and selecting the maximum date_updated. But i am not able to include column type in select statement. When i used type in group by ,the result is not correct as the type is also grouped. How can i query this. Please help

SELECT *
FROM
(SELECT *, ROW_NUMBER() OVER(Partition By con_id ORDER BY date_updated DESC) as seq
FROM
(SELECT * FROM TableA
UNION ALL
SELECT * FROM TableB) as tblMain) as tbl2
WHERE seq = 1

One method:
WITH A AS(
SELECT TOP 1 con_id,
date_updated,
type
FROM TableA
ORDER BY date_updated DESC),
B AS(
SELECT TOP 1 con_id,
date_updated,
type
FROM TableB
ORDER BY date_updated DESC),
U AS(
SELECT *
FROM A
UNION ALL
SELECT *
FROM B)
SELECT *
FROM U;
The 2 CTE's at the top get your most recent rows from the tables, and then the end statement unions them together.
For the benefit of the person who says this doesn't work:
USE Sandbox;
GO
CREATE TABLE tablea (con_id int, date_updated date, [type] tinyint);
CREATE TABLE tableb (con_id int, date_updated date, [type] tinyint);
GO
INSERT INTO tablea
VALUES
(123,'19/06/2018',2),
(123,'15/06/2018',1),
(123,'01/05/2018',3),
(101,'06/04/2018',1),
(101,'05/03/2018',2);
INSERT INTO tableb
VALUES
(123,'15/05/2018',2),
(123,'01/05/2018',1),
(101,'07/06/2018',1);
GO
WITH A AS(
SELECT TOP 1 con_id,
date_updated,
[type]
FROM TableA
ORDER BY date_updated DESC),
B AS(
SELECT TOP 1 con_id,
date_updated,
[type]
FROM TableB
ORDER BY date_updated DESC),
U AS(
SELECT *
FROM A
UNION ALL
SELECT *
FROM B)
SELECT *
FROM U;
GO
DROP TABLE tablea;
DROP TABLE tableb;
This returns the dataset:
con_id date_updated type
----------- ------------ ----
123 2018-06-19 2
101 2018-06-07 1
Which is identical to the OP's data:
con_id date_updated type
--------------------------------------------
123 19/06/2018 2
101 07/06/2018 1

Hope this helps:
WITH combined
AS(
select * FROM tableA
UNION
select * FROM tableB)
SELECT t1.con_id,
t1.date_updated,
t1.type
FROM (
SELECT con_id,
date_updated,
type,
row_number() OVER(partition BY con_id ORDER BY date_updated DESC) AS rownumber
FROM combined) t1
WHERE rownumber = 1;

Can be done using window functions:
declare #TableA table (con_id int, date_updated date, [type] int)
declare #TableB table (con_id int, date_updated date, [type] int)
insert into #TableA values
(123, '2018-06-19', 2)
, (123, '2018-06-15', 1)
, (123, '2018-05-01', 3)
, (101, '2018-04-06', 1)
, (101, '2018-03-05', 2)
insert into #TableB values
(123, '2018-05-15', 2)
, (123, '2018-05-01', 1)
, (101, '2018-06-07', 1)
select distinct con_id
, first_value(date_updated) over (partition by con_id order by con_id, date_updated desc) as con_id
, first_value([type]) over (partition by con_id order by con_id, date_updated desc) as [type]
from
(Select * from #TableA UNION Select * from #TableB) x

Related

SQL: Order by date and distinct Number without losing fields

My knowledge about SQL is not the best but I need a quick solution for this. I have a table
number | date | text
1 | 2018-01-13 | A
2 | 2018-01-15 | B
1 | 2018-02-15 | C
Now I need to remove the duplicate value "number" in the output(select) based on the date. It should look like this:
number | date | text
2 | 2018-01-15 | B
1 | 2018-02-15 | C
I tried
SELECT DISTINCT number, date ORDER BY date DESC FROM table
The problem is that I now miss the field "text" in the output. I also tried
SELECT * DISTINCT(SELECT * number ORDER BY(date) DESC) FROM table
Any ideas?
One option uses ROW_NUMBER:
SELECT number, date, text
FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY number ORDER BY date DESC) rn
FROM yourTable
) t
WHERE rn = 1;
You can achieve it using below query:
SELECT t1.number, t1.date, t1.text
FROM yourTable t1
INNER JOIN
(
SELECT number, MAX(date) AS max_date
FROM yourTable
GROUP BY number
) t2
ON t1.number = t2.number AND
t1.date = t2.max_date;
use below query this will work for ur requirement
SELECT number, date, text
FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY number ORDER BY date DESC) rn
FROM test
) t
WHERE rn = 1;
It can be done just using window functions:
create table #TEST ([NUMBER] int, [DATE] date, [TEXT] char(1))
insert into #TEST values (1, '2018-01-13', 'A'), (2, '2018-01-15', 'B'), (1, '2018-02-15', 'C')
SELECT DISTINCT [NUMBER]
, FIRST_VALUE([DATE]) OVER (PARTITION BY [NUMBER] ORDER BY [DATE] DESC) AS [DATE]
, FIRST_VALUE([TEXT]) OVER (PARTITION BY [NUMBER] ORDER BY [DATE] DESC) AS [TEXT]
FROM #TEST

How can I select distinct by one column?

I have a table with the columns below, and I need to get the values if COD is duplicated, get the non NULL on VALUE column. If is not duplicated, it can get a NULL VALUE. Like the example:
I'm using SQL SERVER.
This is what I get:
COD ID VALUE
28 1 NULL
28 2 Supermarket
29 1 NULL
29 2 School
29 3 NULL
30 1 NULL
This is what I want:
COD ID VALUE
28 2 Supermarket
29 2 School
30 1 NULL
What I'm tryin' to do:
;with A as (
(select DISTINCT COD,ID,VALUE from CodId where ID = 2)
UNION
(select DISTINCT COD,ID,NULL from CodId where ID != 2)
)select * from A order by COD
You can try this.
DECLARE #T TABLE (COD INT, ID INT, VALUE VARCHAR(20))
INSERT INTO #T
VALUES(28, 1, NULL),
(28, 2 ,'Supermarket'),
(29, 1 ,NULL),
(29, 2 ,'School'),
(29, 3 ,NULL),
(30, 1 ,NULL)
;WITH CTE AS (
SELECT *, RN= ROW_NUMBER() OVER (PARTITION BY COD ORDER BY VALUE DESC) FROM #T
)
SELECT COD, ID ,VALUE FROM CTE
WHERE RN = 1
Result:
COD ID VALUE
----------- ----------- --------------------
28 2 Supermarket
29 2 School
30 1 NULL
Another option is to use the WITH TIES clause in concert with Row_Number()
Example
Select top 1 with ties *
from YourTable
Order By Row_Number() over (Partition By [COD] order by Value Desc)
Returns
COD ID VALUE
28 2 Supermarket
29 2 School
30 1 NULL
I would use GROUP BY and JOIN. If there is no NOT NULL value for a COD than it should be resolved using the OR in JOIN clause.
SELECT your_table.*
FROM your_table
JOIN (
SELECT COD, MAX(value) value
FROM your_table
GROUP BY COD
) gt ON your_table.COD = gt.COD and (your_table.value = gt.value OR gt.value IS NULL)
If you may have more than one non null value for a COD this will work
drop table MyTable
CREATE TABLE MyTable
(
COD INT,
ID INT,
VALUE VARCHAR(20)
)
INSERT INTO MyTable
VALUES (28,1, NULL),
(28,2,'Supermarket'),
(28,3,'School'),
(29,1,NULL),
(29,2,'School'),
(29,3,NULL),
(30,1,NULL);
WITH Dups AS
(SELECT COD FROM MyTable GROUP BY COD HAVING count (*) > 1 )
SELECT MyTable.COD,MyTable.ID,MyTable.VALUE FROM MyTable
INNER JOIN dups ON MyTable.COD = Dups.COD
WHERE value IS NOT NULL
UNION
SELECT MyTable.COD,MyTable.ID,MyTable.VALUE FROM MyTable
LEFT JOIN dups ON MyTable.COD = Dups.COD
WHERE dups.cod IS NULL

How to do an inner join to get rid of dupes

How can I use an inner join to get rid of the dupes that I dont want?
The table I'm working on looks like this:
ID Edited_date Status
------------------------
1 1/1/2015 A
1 1/1/2016 B
1 2/1/2016 C
2 1/1/2017 D
2 3/1/2017 B
3 1/1/2016 C
3 4/1/2017 B
3 1/1/2014 D
However, I only want the status of each loan from the most recent edited_date
ID Edited_date Status
------------------------
1 2/1/2016 C
2 3/1/2017 B
3 4/1/2017 B
select * from [table] t1
inner join
(
select ID, max(Edited_date) maxDt
from [Table]
group by ID
) t2
on t1.ID = t2.ID
and t1.Edited_date = t2.maxDt;
For select only:
SELECT *
FROM
(
SELECT *, ROW_NUMBER()OVER(PARTITION BY ID ORDER BY Edited_date desc) as Indicator
FROM TABLE_NAME
) as ABC
WHERE ABC.Indicator = 1
For delete:
WITH ABC
AS
(
SELECT *, ROW_NUMBER()OVER(PARTITION BY ID ORDER BY Edited_date desc) as Indicator
FROM TABLE_NAME
)
DELETE FROM ABC
WHERE ABC.Indicator != 1
using row_number() partitioned by id to get the latest edited_date
select id, edited_date, status
from (
select *
, rn = row_number() over (partition by id order by edited_date desc)
from t
) as s
where rn = 1
top with ties version:
select top 1 with ties
id
, edited_date
, status
from t
order by row_number() over (partition by id order by edited_date desc)
Begin Transaction
Create table #temp (Id int, Edited_date date, Status char(2))
Insert into #temp
Values
('1','1/1/2015','A'),
('1','1/1/2016','B'),
('1','2/1/2016','C'),
('2','1/1/2017','D'),
('2','3/1/2017','B'),
('3','1/1/2016','C'),
('3','4/1/2017','B'),
('3','1/1/2014','D')
Create table #temp2 (Id int, Edited_date date, Status char(2))
Insert into #temp2
Values
('1','2/1/2016','C'),
('2','3/1/2017','B'),
('3','4/1/2017','B')
/** emphasis on the below **/
;with cte as (
Select max(Edited_date) as Edited_date, Status From #temp Group By Status
union all
Select max(Edited_date) as Edited_date, Status From #temp2 Group By Status
)
Select Status, max(Edited_date) as Recent_Edited_date From cte Group By Status
/** End of Emphasis **/
Drop table #temp
Drop table #temp2
Rollback
--- Result ---
Status| Recent_Edited_date
A| 2015-01-01
B| 2017-04-01
C| 2016-02-01
D| 2017-01-01

select distinct list of ids from table with earliest value in same table

I have the following table,
SDate Id Balance
2016-01-01 ABC 3
2016-01-01 DEF 7
2016-01-01 GHI 2
2016-02-01 ABC 6
2016-02-01 DEF 4
2016-02-01 GHI 8
2016-02-01 XYZ 12
I need to write a query that gives me a distinct list of Id's over a date range (so in this example SDate >= '2016-01-01' and SDate <= '2016-02-01') but also give me the earliest balance so the result from the table above I would like to see is,
Id Balance
ABC 3
DEF 7
GHI 2
XYZ 12
Is this possible?
UPDATE
Sorry I should have specified that for each date the Id is unique.
You can do this with a derived table that first works out the minimum SDate value for each Id value. Using this you then join back to your original table to find the Balance for the row that matches those values:
declare #t table(SDate date,Id nvarchar(3),Balance int);
insert into #t values ('2016-01-01','ABC',3),('2016-01-01','DEF',7),('2016-01-01','GHI',2),('2016-02-01','ABC',6),('2016-02-01','DEF',4),('2016-02-01','GHI',8),('2016-02-01','XYZ',12);
declare #StartDate date = '20160101';
declare #EndDate date = '20160201';
with d as
(
select Id
,min(SDate) as MinSDate
from #t
where SDate between #StartDate and #EndDate
group by id
)
select d.Id
,t.Balance
from d
inner join #t t
on(d.Id = t.Id
and d.MinSDate = t.SDate
);
Output:
Id | Balance
----+--------
ABC | 3
DEF | 7
GHI | 2
XYZ | 12
This should be possible with a window function - all you have to do is
partition by id
assign a row number, and
select the top row for each id
Example:
select id,
balance
from (
select id,
balance,
row_number() over( partition by id order by SDate ) as row_num
from table1
where SDate between '2016-01-01' and '2016-02-01'
) as a
where row_num = 1
Note: the advantage of this method is it is a lot more flexible. Say you wanted the 2 oldest records, you could just change to where row_num <= 2.
Analytic row_number() should be the fastest
select *
from (
select
t.*,
row_number() over (partition by Id order by SDate) rn
from your_table t
) t where rn = 1;
You can achieve this with a self join, which may not be the fastest or most elegant solution:
CREATE TABLE #SOPostSample
(
SDate DATE ,
Id NVARCHAR(5) ,
Balance INT
);
INSERT INTO #SOPostSample
( SDate, Id, Balance )
VALUES ( '2016-01-01', 'ABC', 3 ),
( '2016-01-01', 'DEF', 7 ),
( '2016-01-01', 'GHI', 2 ),
( '2016-02-01', 'ABC', 6 ),
( '2016-02-01', 'DEF', 4 ),
( '2016-02-01', 'GHI', 8 ),
( '2016-02-01', 'XYZ', 12 );
SELECT t1.Id ,
MIN(t2.Balance) Balance
FROM #SOPostSample t1
INNER JOIN #SOPostSample t2 ON t1.Id = t2.Id
GROUP BY t1.Id ,
t2.SDate
HAVING t2.SDate = MIN(t1.SDate);
DROP TABLE #SOPostSample;
Produces:
id Balance
============
ABC 3
DEF 7
GHI 2
XYZ 12
This works for the sample data, but please test with more data as I just wrote it quickly.
This should work, Top 1 just inserted for safety, should not be needed if SDate and Id are unique in combination
SELECT o.Id ,
( SELECT TOP 1
Balance
FROM tbl
WHERE Id = o.Id
AND SDate = MIN(o.SDate)
) Balance
FROM tbl o
GROUP BY Id
HAVING sDate BETWEEN '20160101' AND '20160201';
You can use sub-query
SELECT Id ,
( SELECT TOP 1
Balance
FROM [TableName] AS T1
WHERE T1.Id = [TableName].Id
ORDER BY SDate
) AS Balance
FROM [TableName]
GROUP BY Id;

Conditional selection of RowNum in SQL

I have written a query which returns me following data.
ID EmpFirstName EmpLastName RowNum
1 X Y 1
2 A B 1
3 A B 2
Now I want all records where RowNum is >1. For example, in this case I need 2 and 3 record in output.
If I put condition RowNum >1 then I will get only third record but I want 2 as well.
Assuming your query is this:
select ID, EmpFirstName, EmpLastName,
ROW_NUMBER() OVER (PARTITION BY EmpFirstName, EmpLastName ORDER BY ID) AS RowNum
FROM aTable
This is a classic query used to filter out any duplicate values.
In order effectively select all the records with the duplicate values I can suggest using the COUNT() window function:
;with a as (
select ID, EmpFirstName, EmpLastName,
ROW_NUMBER() OVER (PARTITION BY EmpFirstName, EmpLastName ORDER BY ID) AS RowNum,
COUNT(*) OVER (PARTITION BY EmpFirstName, EmpLastName) AS cnt
FROM aTable
)
SELECT * FROM a where cnt > 1
ORDER BY EmpFirstName, EmpLastName
To test it use this query:
drop table #tmp
CREATE table #tmp (ID int , EmpFirstName varchar(10) , EmpLastName varchar(10))
go
INSERT INTO #tmp VALUES
(1,'X','Y' )
,(2,'A','B')
,(3,'A','B')
,(4,'A','C')
,(5,'B','C')
,(6,'B','C')
;with a as (
select ID, EmpFirstName, EmpLastName,
ROW_NUMBER() OVER (PARTITION BY EmpFirstName, EmpLastName ORDER BY ID) AS RowNum,
COUNT(id) OVER (PARTITION BY EmpFirstName, EmpLastName) AS cnt
FROM #tmp
)
SELECT * FROM a where cnt > 1
ORDER BY EmpFirstName, EmpLastName
Result:
ID EmpFirstName EmpLastName RowNum cnt
----------- ------------ ----------- -------------------- -----------
2 A B 1 2
3 A B 2 2
5 B C 1 2
6 B C 2 2
I make sample data and use this query
CREATE table #tmp (ID int , EmpFirstName varchar(10) , EmpLastName varchar(10) ,RowNum int)
INSERT INTO #tmp VALUES
(1,'X','Y',1)
,(2,'A','B',1)
,(3,'A','B',2)
SELECT ID,EmpFirstName,EmpLastName,RowNum
FROM (
SELECT *
,ROW_NUMBER() OVER (ORDER BY ID) AS [NEWrownum]
FROM #tmp
) q
WHERE q.NEWrownum > 1
try this,
DECLARE #Result TABLE (ID INT, EmpFirstName VARCHAR(10), EmpLastName VARCHAR(10), RowNum INT)
INSERT INTO #Result
VALUES
(1, 'X', 'Y', 1)
,(2, 'A', 'B', 1)
,(3, 'A', 'B', 2)
SELECT r1.*
FROM #Result r1
INNER JOIN (SELECT * -- get duplicate records
FROM #Result
WHERE RowNum = 2
) as r2 ON r1.EmpFirstName = r2.EmpFirstName
AND r1.EmpLastName = r2.EmpLastName