Joins and Aggregate Functions - sql

I have 3 tables I need to join together. Once I join the first two, I'm going to have two columns, let's call them A and B.
The relationship between A and B is many-to-many. So we can have:
A B
1 1
1 2
2 1
2 3
Then, I need to join with a third table on the B column, giving me:
A B C
1 1 5
1 2 6
2 1 9
2 3 2
Now for my final result I only want one row for each unique A value, and I want to select that row based upon the MAX C value across that given A.
So in this example the final value would be:
A B C
1 2 6
2 1 9
I have the following query which works as expected, but I am fairly certain it is not the best way of doing it:
SELECT
Temp.A,
Temp.B,
Temp.C1
FROM
(SELECT DISTINCT
T1.A,
T2.B,
MAX(T3.C) OVER(PARTITION BY T1.A) AS C1
FROM T1
INNER JOIN T2 ON T1.X = T2.X
INNER JOIN T3 ON T2.B = T3.B) Temp
INNER JOIN T3 ON T3.B = Temp.B
WHERE Temp.C1 = T3.C

You query can be simplified:
You don't need select distinct in the subquery.
You don't need to join back to T3.
You can select the C value in the subquery.
Here is the revision:
SELECT Temp.A, Temp.B, Temp.C
FROM (SELECT T1.A, T2.B, T3.C, MAX(T3.C) OVER (PARTITION BY T1.A) AS C1
FROM T1 INNER JOIN
T2
ON T1.X = T2.X INNER JOIN
T3
ON T2.B = T3.B
) Temp
WHERE Temp.C1 = Temp.C
Do note that if T3 has duplicate maximum values, then this will return duplicates. To get just one, you can use row_number() instead:
SELECT Temp.A, Temp.B, Temp.C
FROM (SELECT T1.A, T2.B, T3.C,
ROW_NUMBER() OVER (PARTITION BY T1.A ORDER BY T3.C DESC) AS seqnum
FROM T1 INNER JOIN
T2
ON T1.X = T2.X INNER JOIN
T3
ON T2.B = T3.B
) Temp
WHERE seqnum = 1;

Related

Multiple search between two tables

I have two tables - the first (this is just an example but the original query is a pretty big)
item_id_t1
serial_num_t1
country_t1
customer
snapshot_date_t1
serial_num_trunc_t1
156648107
222-99950578
AAA
BBSS
12/1/2022
99950578
156648107
222-99950578
AAA
BBSS
11/1/2022
99950578
156648107
222-99950578
AAA
BBSS
1/1/2023
99950578
108279887
888-515179765
AAA
BBSS
12/1/2022
515179765
108279887
888-515179765
AAA
BBSS
11/1/2022
515179765
108279887
888-515179765
AAA
BBSS
11/1/2023
515179765
and the second one
serial_num_trunc_t2
serial_num_t2
up_ind_t2
99950578
333-99950578
1
515179765
888-515179765
1
Now, my first step would be to find a match based on serial_num_t1 = serial_num_t2 and the second part of the code should be to find all unmatched records but this time based on serial_num_trunc_t1 = serial_num_trunc_t2.
I started with CTE tables first
WITH t1 AS (SELECT * FROM t1),
t2 AS (SELECT * FROM t2)
SELECT a.*
(SELECT t1.*, t2.*
FROM t1
LEFT JOIN t2
ON serial_num_t1 = serial_num_t2) a
INNER JOIN
(SELECT t1.*, t2.*
FROM t1
LEFT JOIN t2
ON t1.serial_num_trunc_t1 = t2.serial_num_trunc_t1
WHERE q.up_ind_t2 <> 1) b
ON a.serial_num_trunc_t1 = b.serial_num_trunc_t2
and here I am stuck. How to compare unmatched values from table "a" again with table "b" based ON t1.serial_num_trunc_t1 = t2.serial_num_trunc_t2 WHERE t2.ip_ind_t2 <> 1
My final table should have six records (like t1) and "up_ind_t2" should be filled with 1 for all six records in the final table.
I would appreciate your help.
I am guessing, you want
WITH t1 AS (SELECT * FROM t1),
t2 AS (SELECT * FROM t2)
SELECT t1.*, t2.*
FROM t1
LEFT JOIN t2
ON serial_num_t1 = serial_num_t2
UNION ALL
SELECT t1.*, t2.*
FROM t1
INNER JOIN t2
ON t1.serial_num_trunc_t1 = t2.serial_num_trunc_t1
WHERE q.up_ind_t2 <> 1
as Query,the LEft JOIN doesn't make that much sense in the firstplace, so i removed ot from the second.
the second query implies that you have only 1 record for every serial_num_trunc_t2 and that is not equal to 1, if not you need to add limit
#nbk
It doesn't look like my solution. I added match_level in order to see from where a match comes: from LEFT JOIN or from INNER JOIN (UNION part of the code).
So, when I run your code I get the same results as before UNION ALL part
WITH t1 AS (SELECT * FROM ib),
t2 AS (SELECT * FROM qu)
SELECT t1.*, t2.*, 1 as match_level
FROM ib t1
LEFT JOIN qu t2
ON serial_num_t1 = serial_num_t2
UNION ALL
SELECT t1.*, t2.*, 2 as match_level
FROM ib t1
INNER JOIN qu t2
ON t1.serial_num_trunc_t1 = t2.serial_num_trunc_t2
WHERE t2.up_ind_t2 <> 1
Finally, this is what I needed.
with
ib as (
select *
from [dbo].[ib] i
left join [dbo].[qu] q
on i.serial_num_t1=q.serial_num_t2),
qu as (
select *
from [dbo].[ib] i
right join [dbo].[qu] q
on i.serial_num_trunc_t1=q.serial_num_trunc_t2)
select ff.*
from
(select *
from
(select ROW_NUMBER() OVER(PARTITION BY q.item_id_t1,
q.serial_num_t1,q.country_t1, q.customer, q.snapshot_date_t1,
q.up_ind_t2 ORDER BY q.snapshot_date_t1, q.up_ind_t2 DESC) AS it_row, q.*
from qu q
left join ib b
on q.serial_num_trunc_t2 = b.serial_num_trunc_t1) a
union
select 99 as it_row, zz.*
from qu as zz ) ff
where it_row = 1

SQL - left join table to itself

In short, want to left join table to itself, but only rows that have id 1 or 2
Table:
id f1
1 a
1 b
2 a
3 a
expected result
1 2 a a
1 2 b null
does not work:
select t1.id,t2.id, t1.f1,t2.f1
from table t1
left join table t2 on t1.f1 = t2.f1 and t1.id = 1 and t2.id = 2
Any idea ?
This construct is a bit strange, but you need to filter on the first table in where:
select t1.id, t2.id, t1.f1, t2.f1
from table t1 left join
table t2
on t1.f1 = t2.f1 and t2.id = 2
where t1.id = 1 ;
The general rule for a left join is that conditions on the first table go in the where clause. Conditions on the second table go in the on clause.

Postgresql SELECT LEFT JOIN with columns on case

SELECT a1,a2,a3,a4,count(a5),b1,b2,b3
FROM table1
LEFT JOIN table2 ON a1=b1 AND a2=b2 (*here i need to join
next columns a3=b3 only if from table2 will be returned more than 1 records
other wise first 2 columns will be enough*)
group by a1,a2,a3,a4,a5,b1,b2,b3
Anybody knows how to perform this trick ?
Well, if I understand correctly:
FROM table1 t1 LEFT JOIN
(SELECT t2.*, COUNT(*) OVER (PARTITION BY b1, b2) as cnt
FROM table2 t2
)
ON t1.a1 = t2.b1 AND t1.a2 = t2.b2 AND
(cnt = 1 OR t1.a3 = t2.a3)

Oracle subquery top 1 result

I want to get the top 1 row for each unique value of b with the minimum value of c for that particular value of b. Even though there can be more than 1 row with the same min value (just chose the first one)
myTable
a integer (unique)
b integer
c integer
I've tried this query
SELECT t1.*
FROM myTable t1,
(SELECT b,
MIN(c) as c
FROM myTable
GROUP BY b) t2
WHERE t1.b = t2.b
AND t1.c = t2.c
However, in this table it's possible for there to be more than 1 instance of the minimum value of c for a given value of b. The above query generates duplicates under these conditions.
I've got a feeling that I need to use rownum somewhere, but I'm not quite sure where.
You can use ROW_NUMBER:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY b ORDER BY c) AS rn
FROM myTable
) AS T1
WHERE rn = 1
To tie-break between the equal c's, you will need to subquery one level further to get the min-a for each group of equal c's per b. (A mouthful!)
select t0.*
FROM myTable t0
inner join (
select t1.b, t1.c, MIN(a) as a
from myTable t1
inner join (
select b, min(c) as c
from myTable
group by b
) t2 on t1.b = t2.b and t1.c = t2.c
group by t1.b, t1.c
) t3 on t3.a = t0.a and t3.b = t0.b and t3.c = t0.c

SQL Server Max statement returns multiple results

When querying two tables (t1, t2) and using a MAX statement for a column in t2, SQL returns multiple entries.
This seems to be because I also query other info from t2 which consists of unique entries.
Simplified table example
t1.number t2.number_id t2.sync_id t2.text
1 1 1 'My problem is
1 1 2 That
2 2 3 Multiple entries
2 2 1 Are
2 2 2 Returned'
When using
SELECT t1.number, max(t2.sync_id), convert(varchar(100),t2.text)
FROM t1, t2
WHERE t1.number = t2.number_id
GROUP BY t1.number, convert(varchar(100),t2.text)
I get multiple entries instead of just line 2 and 5 from the example table.
You need to remove convert(varchar(100),t2.text) from GROUP BY t1.number, convert(varchar(100),t2.text). You are now grouping by two criteria when you just want grouping by t1.number.
Also you may be interested on how to get all the text in the t2.text column in one single row. If so take a look here: http://databases.aspfaq.com/general/how-do-i-concatenate-strings-from-a-column-into-a-single-row.html
Assuming at least SQL 2005 so you can use a CTE:
;with cteMaxSync as (
select t1.number, max(t2.sync_id) as MaxSyncId
from t1
inner join t2
on t1.number = t2.number_id
group by t1.number
)
select c.number, c.MaxSyncId, convert(varchar(100),t2.text)
from cteMaxSync c
inner join t2
on c.number = t2.number_id
and c.MaxSyncId = t2.sync_id
Standard sql approach
SELECT
t1.number,
t2.sync_id,
convert(varchar(100),t2.text)
FROM
t1
INNER JOIN t2
ON t1.number = t2.number_id
INNER JOIN (
SELECT Max(t2.synch_id) synch_id, t1.number)
FROM t1
INNER JOIN t2
ON t1.number = t2.number_id ) max
ON t1.number = max.number and
t2.synch_id = max.synch_id