Join and select column with max value - sql

I am working on two tables in Oracle, TABLE_A containing unique IDs and Balance (number). TABLE_B shows, for the same IDs, the specific transactions and contains the following fields: IDs (not unique), BAL, Sequence_number.
I want to check that TABLE_A.Balance is always equal to TABLE_B.Balance having Max(Sequence_number).
So I expect to have just one row for each ID.
I've tried the following, yet it does not return a unique row for each ID but multiples. Why is that?
Select a.ID, a.Balance,b.Balance, b.sequence_number From TABLE_A a Inner join (select ID, Balance, max(sequence_number) as sequence_number from TABLE_B group by ID, Balance) b On a.ID = B.ID Group by a.ID, a.Balance, b.Balance, b.sequence_number
TABLE_A
ID_______Balance
1_______10
2_______15
3_______50
TABLE_B
ID____Balance____Sequence_number
1_______19_______1
1_______75_______2
1_______10_______3
2_______39_______1
2_______15_______2
3_______120_______1
3_______89_______2
3_______57_______3
3_______50_______4

You can use window functions:
select a.*, b.balance
from a left join
(select b.*,
row_number() over (partition by id order by sequence_number desc) as seqnum
from b
) b
on b.id = a.id and b.seqnum = 1;
I'm not quite sure what you want to compare, but this returns every row in a with the row in b that has the highest sequence number.

You can use row_number()over() window function to get the row having ID wise highest sequence_number.
Schema:
create table TABLE_A(ID int, Balance int);
insert into TABLE_A values(1,10);
insert into TABLE_A values(2,15);
insert into TABLE_A values(3,50);
create table TABLE_B (ID int , Balance int,Sequence_number int);
insert into TABLE_B values(1,19,1);
insert into TABLE_B values(1,75,2);
insert into TABLE_B values(1,10,3);
insert into TABLE_B values(2,39,1);
insert into TABLE_B values(2,15,2);
insert into TABLE_B values(3,120,1);
insert into TABLE_B values(3,89,2);
insert into TABLE_B values(3,57,3);
insert into TABLE_B values(3,50,4);
GO
Query:
with cte as
(
select id, balance, sequence_number, row_number()over (partition by id order by sequence_number desc)rnk from table_b
)
Select a.ID, a.Balance,b.Balance, b.sequence_number From TABLE_A a
Inner join cte b on a.id=b.id and rnk=1;
GO
Output:
ID
Balance
Balance
sequence_number
1
10
10
3
2
15
15
2
3
50
50
4
db<>fiddle here

Related

SQL Full Outer Join without duplicates with like condition

Following are two tables.
I want to find and join records between two tables with having value -1 or +1.
But in that case it returns me duplicate records also.
How can i get the result without duplicate records?
drop table if exists #A
CREATE TABLE #A(ID float, Category varchar(10), Amount float )
insert into #A values
(1,'A',150.4),
(2,'A',151.0),
(3,'A',149.8),
(4,'A',165.0),
(5,'A',165.0)
drop table if exists #B
CREATE TABLE #B(BID float, BCategory varchar(10), BAmount float )
insert into #B values
(95,'A',151),
(101,'A',150),
(115,'A',165.0),
(118,'A',165.0)
i have tried following query, which returns duplicates.
select *
from
(select
ID, category, Amount,
row_number() over (partition by category, Amount order by category, Amount) as Sr
from #A) A
full outer join
(select
BID, Bcategory, BAmount,
row_number() over (partition by Bcategory, BAmount order by Bcategory, BAmount) as Sr
from #B) B on a.category = b.bCategory
and a.amount between b.bamount - 1 and b.BAmount + 1
and a.sr = b.sr
Logic: How the current user is mapping manually:-
First, try to Exact match (category and amount) with another table.
Then whatever left try to match Category and Amount with (+/- 1) of TableB.
So 149.8 or 150.4 of A, both any can join with 150 of B. Since only one (150) in Table B is left and 151 already assigned to 151 under exact match, one record of A will be join to null.
Let's say, since 150.4 is appearing first in tableA it can go with 150 of TableB.
And 149.8 will remain unmatched. Practically user does not mind to match with either or. Important is, any one row (150.4 or 149.8) shall match to null. Problem with left or full outer join is, 150 of B is being assigned to both (149.8 and 150.4).
If you want unique record we need to mapped relation between the table
Table Definition
drop table if exists #A
CREATE TABLE #A(ID float, Category varchar(10), Amount float )
insert into #A values
(1,'A',150.4),
(2,'A',151.0),
(3,'A',149.8),
(4,'A',165.0),
(5,'A',165.0)
drop table if exists #B
CREATE TABLE #B(AID float,BID float, BCategory varchar(10), BAmount float )
insert into #B values
(2,95,'A',151),
(1,101,'A',150),
(4,115,'A',165.0),
(5,118,'A',165.0)
Query
SELECT * FROM #A A
LEFT JOIN #B B ON A.ID = B.AID
Other way
select *
from
(select
ID, category, Amount,
row_number() over (partition by category, Amount order by category, Amount) as Sr
from #A) A
full outer join
(select
AID,BID, Bcategory, BAmount,
row_number() over (partition by Bcategory, BAmount order by Bcategory, BAmount) as Sr
from #B) B on a.category = b.bCategory
and a.amount between b.bamount - 1 and b.BAmount + 1
and a.sr = b.sr AND A.ID = B.AID
OutPut result

How to join only the most recent record in SQL Server 2008?

I have two tables A and B. Table A can have multiple records for each user. Table B can have multiple records for each row in table A.
Here is an example:
Table A Table B
a_id a_key b_id b_key b_AtblID
1. 6678 5778 1. 6509 5778 6678
2. 6679 5778 2. 6508 5778 6678
3. 6507 5778 6679
4. 6505 5778 6679
5. 6490 5778 6678
You can see 2 records in A table. One record has 3 records in B table and one have 2 records in B table.
I want my query to pull records from table A and the most recent record from B table. I don't need every single record from Table B that is matching record in table A if that makes sense. Here is example of what I have but my code pulled all records from table B:
SELECT *
FROM A
LEFT OUTER JOIN B
ON b_AtblID = (
SELECT TOP 1 b_AtblID
FROM B
WHERE b_AtblID = a_id
ORDER BY b_id DESC
)
I was hoping that my code will return only one record for each row on table A but I got all records from table B. If anyone can help please let me know. Thanks.
You can use ROW_NUMBER:
WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY b_AtblID ORDER BY b_id DESC)
FROM dbo.TableB
)
SELECT *
FROM dbo.TableA a
INNER JOIN CTE b
ON a.a_id = b.b_AtblID
AND b.RN = 1
;
Or CROSS APPLY:
SELECT *
FROM dbo.TableA a
CROSS APPLY (SELECT TOP 1 *
FROM dbo.TableB
WHERE b_AtblID = a.a_id
ORDER BY b_id DESC) b;
Create table #tempA (a_id int,a_key int)
Create table #tempB (b_id int,b_key int,b_abtlid int)
Insert into #tempA values(6678,5778),(6679,5778)
Insert into #tempB values(6509,5778,6678),(6508,5778,6678),(6507,5778,6679),(6505,5778,6679),(6490,5778,6678)
select a_id,a_key,b_id,b_abtlid from (
select *,row_number() over(partition by a_id order by b_id desc )as Rn from #tempA a
inner join #tempB b
on a.a_id=b.b_abtlid ) t
where t.Rn=1

SQL Hide/Show rows based on row count from another table

I have a SQL question. I have two tables, tableA has 4 records, tableB has 0 records right now, but will go over 200 total records. I was wondering if there is away to hide the last two records of tableA if tableB is under 200 records?
What I got so far is very simple
SELECT
id, dateSlot, timeSlot
FROM
tableA a
INNER JOIN
tableB b ON a.id = b.dateTimeSlotId;
I just don't know how to hide records based on another tables total records.
Can anyone help?
It is only an idea. if the tables are not symmetric you need to improve the logic.
declare #tableA table (id int)
declare #tableB table (dateTimeSlotId int , dateSlot date, timeSlot time)
insert #tableA values (1),(2),(3),(4),(5),(6),(7)
insert #tableB values
(1,'20170801', '00:00'),
(2,'20170802', '00:01'),
(3,'20170803', '00:02'),
(4,'20170804', '00:03'),
(5,'20170805', '00:04'),
(6,'20170806', '00:05'),
(7,'20170807', '00:06')
;with cte as(
SELECT ROW_NUMBER() over (order by id) rNumber, id, dateSlot, timeSlot
FROM #tableA a INNER JOIN
#tableB b
ON a.id = b.dateTimeSlotId)
SELECT id, dateSlot, timeSlot
FROM cte where rNumber <= (SELECT case when Count(1) >= 200 then Count(1) -2 else Count(1) end from #tableB)

getting top row of joined table

I have 2 tables, tableA and tableB
tableA - id int
name varchar(50)
tableB - id int
fkid int
name varchar(50)
Both tables are joined between id and fkid.
Below are sample rows from tableA
Below is output from tableB
I want to join both tables and get only top row of joined table. So output will be like below
Id Name fkid
1 P1 1
2 P2 4
3 P3 null
Here is Sql fiddle
How can i achieve this with single query? I know that i can loop through in my .net code and retrieve top rows. But i want it in single query.
select a.id,a.name,b.fid from tableA a left join
(
select min(id) fid ,fkid from tableB group by fkid
)b
on a.id = b.fkid
select ta.id, ta.name, min(tb.id) from tableA ta
left join tableB tb on tb.fkid=ta.id
group by ta.id, ta.name
You could do this:
;WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(PARTITION BY fkID ORDER BY ID) AS RowNbr,
tableB.*
FROM
tableB
)
SELECT
*
FROM
tableA
LEFT JOIN CTE
ON CTE.fkID=tableA.id
AND CTE.RowNbr=1
Demo here
Or without window function. Like this:
SELECT
*
FROM
tableA
LEFT JOIN
(
SELECT
ROW_NUMBER() OVER(PARTITION BY fkID ORDER BY ID) AS RowNbr,
tableB.*
FROM
tableB
) as tbl
ON tbl.fkID=tableA.id
AND tbl.RowNbr=1
Demo here
Update:
The reason why I choose to do it with row_number is that if there is more columns in tableB then the example. Then there is no need for additional aggregate if you want to show more columns. For me personally it is more clear with an order by on the ID

SQL: How to find the minimum number of entries linked from one table to another

Let's say I have some Table_A:
A_id | A_val
1 a
2 b
3 c
Some Table_B:
B_id | B_val
1 d
2 e
3 g
and a linker Table_C:
A_id | B_id
1 1
2 1
2 2
3 1
3 2
3 3
I'm in need of help trying to find the items in Table A that has the fewest items in Table B linked to it. I'm currently a beginner with SQL using PostgreSQL and figured it may have something to do with using a sub-query. I've managed to count the links using:
SELECT A_id, COUNT(B_id) as Num_links
FROM TABLE_C
GROUP BY A_id;
But I've no idea where to go from here.
You could use a with clause to give an alias to your "count" query and treat it like a temp table. Then select the a_id with the num_links less-than-or-equal-to the lowest count in num_links.
WITH link_counts AS (
SELECT a_id, COUNT(b_id) as num_links
FROM table_c
GROUP BY a_id
)
SELECT a_id
FROM link_counts
WHERE num_links <= (SELECT MIN(num_links) FROM link_counts)
Note that this could return multiple rows if different a_id have same (lowest) number of links (for instance if a_id 1 and 4 both only had 1 link each).
You can use RANK(). This will rank your Aid's by the COUNT(Bid) -- for those that have the same number, all will be returned with the same rank.
SELECT *
FROM A T1
JOIN (
SELECT Aid, RANK() OVER (ORDER BY COUNT(Bid)) rnk
FROM C
GROUP BY Aid
) T2 ON T1.Id = T2.Aid
WHERE T2.rnk = 1
And here is the Fiddle.
Good luck.
Here's another option. It uses a subquery in the HAVING clause:
SELECT DISTINCT AId, COUNT(*)
FROM C
GROUP BY AId
HAVING COUNT(*) <= ALL (SELECT COUNT(*)
FROM C
GROUP BY AId)
And the related fiddle. I have no idea how this would compare to the other solutions in terms of performance, but it seems to show clearly what is going on.
Here is the strategy. Calculate the maximum number of links. You can do that by revising your query with an order by and limit.
Next, calculate the total number of links for each row in tableC. For this, I'm using a window function. The statement:
count(*) over (partition by a_id)
Says create a variable that is the count of "a"s in my table.
Then join this together.
select distinct c.a_id
from (select c.*,
count(*) over (partition by a_id) as num_links
from table_c c
) c join
(select a_id, count(*) as num_links
from table_c c
group by a_id
order by 2 asc
limit 1
) cmax
on c.num_links = cmax.num_links
WITH ct AS (
SELECT a.a_id
,count(c.a_id) AS link_ct
,min(count(c.a_id)) OVER () AS min_ct
FROM table_a a
LEFT JOIN table_c c USING (a_id)
GROUP BY 1
)
SELECT a_id, link_ct
FROM ct
WHERE link_ct = min_ct;
This is similar to what #matts posted. It differs in some aspects:
In the CTE ct I count LEFT JOIN to table_c, this way I don't miss rows from table_a with 0 connections to table_b which should win according to the definition in the question.
Compute min_ct in the CTE with a window function (and therefore without additional subquery in the final WHERE condition). May or may not be faster, it's cleaner in any case.
Final WHERE condition is good with = instead of <=.
->sqlfiddle demonstrating the difference.
It looks like others here have a more elegant solution... my SQL Fu is a little rusty but this will work as well.
CREATE TABLE Table_C
(
A_id INT,
B_id INT
);
INSERT INTO Table_C (A_id, B_id) VALUES (13, 112);
INSERT INTO Table_C (A_id, B_id) VALUES (44, 105);
INSERT INTO Table_C (A_id, B_id) VALUES (66, 68);
INSERT INTO Table_C (A_id, B_id) VALUES (13, 113);
INSERT INTO Table_C (A_id, B_id) VALUES (445, 105);
INSERT INTO Table_C (A_id, B_id) VALUES (660, 68);
CREATE TABLE TempTable
(
A_id INT,
Cnt INT
);
INSERT INTO
TempTable (A_id, Cnt)
SELECT
t.A_id
, COUNT(t.A_id) AS Cnt
FROM
Table_C t
GROUP BY
t.A_id;
SELECT #minCnt := MIN(Cnt) FROM TempTable;
SELECT
A_id
FROM
Table_C
GROUP BY
A_id
HAVING
COUNT(A_id) = #minCnt;