How to select rows by max value from another column in Oracle - sql

I have two datasets in Oracle Table1 and Table2.
When I run this:
SELECT A.ID, B.NUM_X
FROM TABLE1 A
LEFT JOIN TABLE2 B ON A.ID=B.ID
WHERE B.BOOK = 1
It returns this.
ID NUM_X
1 10
1 5
1 9
2 2
2 1
3 20
3 11
What I want are the DISTINCT ID where NUM_X is the MAX value, something like this:
ID NUM_x
1 10
2 2
3 20

You can use aggregation:
SELECT A.ID, MAX(B.NUM_X)
FROM TABLE1 A LEFT JOIN
TABLE2 B
ON A.ID = B.ID
WHERE B.BOOK = 1
GROUP BY A.ID;
If you wanted additional columns, I would recommend window functions:
SELECT A.ID, MAX(B.NUM_X)
FROM TABLE1 A LEFT JOIN
(SELECT B.*,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY NUM_X DESC) as seqnum
FROM TABLE2 B
) B
ON A.ID = B.ID AND B.seqnum = 1
WHERE B.BOOK = 1
GROUP BY A.ID;

Related

Need Full Outer Join without having Cross Join

Need to join two table without having cross join between them.
The join condition need to be made on Tabl.month = Tab2.month
Input
Table1 Table2
Month ID Month ID
1 a 1 a
1 b 1 b
1 c 2 g
2 d 3 i
2 e 3 j
3 f 3 k
Output:
Month_Tab1 ID_Tab1 Month_Tab2 ID_Tab2
1 a 1 a
1 b 1 b
1 c Null Null
2 d 2 g
2 e Null Null
3 f 3 i
Null Null 3 j
Null Null 3 k
The above o/p is required, without cross join, have tried full outer but cross join is happening as the ID is duplicate in both Tables. Left/Right join also cannt be applicable as either of the table might have larger set of ID's.
You want a full join, but with row_number() to identify the matches:
select t1.month month_tab1, t1.id id_tab1, t2.month month_tab2, t2.id id_tab2
from (
select t.*, row_number() over(partition by month order by id) rn from table1 t
) t1
full join (
select t.*, row_number() over(partition by month order by id) rn from table2 t) t2
on t2.month = t1.month and t2.rn = t1.rn
You can use a full outer join:
select
a.month,
a.id,
b.month,
b.id
from (
select month, id,
row_number() over(partition by month order by id) as n
from table1
) a
full outer join (
select month, id,
row_number() over(partition by month order by id) as n
from table2
) b on b.month = a.month and b.n = a.n
order by coalesce(a.month, b.month), coalesce(a.n, b.n)

Group by the union of two columns

How can GROUP BY based on the union of two columns be achieved performantly? There may be NULL values in either column. Something like (obviously this doesn't work):
SELECT a.val, b.val
FROM a
LEFT JOIN b on a.id = b.id
GROUP BY UNION(a.val, b.val)
With results like:
a.val | b.val
-----------
1 1
2 2
NULL 3
4 NULL
5 5
Thanks!
Why can't you use NVL
SELECT NVL(a.val, b.val) FROM a LEFT JOIN b on a.id = b.id
GROUP BY NVL(a.val, b.val)

Count similar values from table by combining two tables

I have two table
table A
name id
ABC 1
PQR 2
XYZ 1
QWE 2
DFG 3
Another table
table B
id idname
1 stuart
2 bob
3 alex
expected output
id idname count
1 stuart 2
2 bob 2
3 alex 1
Iam using oracle 9i, Is it possible to obtain the expected result?
I have tried using distinct keyword but its not helping as it provides only the total count
That's simple. Join and count:
select b.id,
b.idname,
count(*) as cnt
from table_a a
join table_b b on a.id = b.id
group by b.id, b.idname;
If you need all the record from table b even if there is no corresponding row in table a, you can use an outer join:
select b.id,
b.idname,
count(a.id) as cnt
from table_a a
right join table_b b on a.id = b.id
group by b.id, b.idname;
Same can be achieved by using a left join:
select b.id,
b.idname,
count(a.id) as cnt
from table_b b
left join table_a a on a.id = b.id
group by b.id, b.idname;
Use JOIN to get data from both tables and use the aggregate function COUNT with GROUP BY.
Query
select t1.id, t1.idname, count(t2.name) as count
from TableB t1
left join TableA t2
on t1.id = t2.id
group by t1.id, t1.idname
order by count(t2.name) desc, t1.id;;

Different output when using count and group by

When trying to get a count of IDs I get a different answer when grouping by day vs when I am not.
select cv.CONV_DAY, count(distinct cv.CLICK_ID)
from
clickcache.click cc
right join(
select distinct cv.CLICK_ID, cv.CONV_DAY, cv.PIXEL_ID
from clickcache.CONVERSION cv
where cv.CLICK_ID IS NOT NULL) cv ON cv.CLICK_ID = cc.ID
where cc.ADV_ACCOUNT_ID = 25176
and cv.CONV_DAY between '2016-8-01' AND '2016-08-07'
and AMP_CLICK_STATUS_ID = 1
AND pixel_id IN
(SELECT DISTINCT conversion_pixel_id
FROM
ampx.campaign_event_funnel ef
JOIN ampx.campaign cp ON
cp.id = ef.campaign_id
AND cp.campaign_status_id = 1
WHERE
ef.account_id IN(25176)
AND include_optimization = 1 )
group by 1
order by 1 asc
This yields 170 which is the correct answer and the I want. This, on the other hand, displays 157.
select count(distinct cv.CLICK_ID)
from
clickcache.click cc
right join(
select distinct cv.CLICK_ID, cv.CONV_DAY, cv.PIXEL_ID
from clickcache.CONVERSION cv
where cv.CLICK_ID IS NOT NULL) cv ON cv.CLICK_ID = cc.ID
where cc.ADV_ACCOUNT_ID = 25176
and cv.CONV_DAY between '2016-8-01' AND '2016-08-07'
and AMP_CLICK_STATUS_ID = 1
AND pixel_id IN
(SELECT DISTINCT conversion_pixel_id
FROM
ampx.campaign_event_funnel ef
JOIN ampx.campaign cp ON
cp.id = ef.campaign_id
AND cp.campaign_status_id = 1
WHERE
ef.account_id IN(25176)
AND include_optimization = 1 )
My question is why do I get this discrepancy and how to fix it to get a proper count?
Thank you!
Your count dependents from right query, maybe you have duplicate row?
example
table1
id name value
1 2 3
table2
id name value
1 4 5
2 6 3
1 6 3
right join tables on value get result
select * from table1 a right join table2 b on a.value = b.value
1 2 3 2 6 3
1 2 3 1 6 3
select count(distinct a.value)
from (select a.id, a.name, a.value, b.id, b.name, b.value
from table1 a right join table2 b on a.value = b.value)
result is 1
select b.id, count(distinct a.value)
from (select a.id, a.name, a.value, b.id, b.name, b.value
from table1 a right join table2 b on a.value = b.value group)
group by b.id
result is two rows
2 1
1 1
My guess is that, you have a problem for this reason.

SQL aggregation query, grouping by entries in junction table

I have TableA in a many-to-many relationship with TableC via TableB. That is,
TableA TableB TableC
id | val fkeyA | fkeyC id | data
I wish the do select sum(val) on TableA, grouping by the relationship(s) to TableC. Every entry in TableA has at least one relationship with TableC. For example,
TableA
1 | 25
2 | 30
3 | 50
TableB
1 | 1
1 | 2
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
should output
75
30
since rows 1 and 3 in Table have the same relationships to TableC, but row 2 in TableA has a different relationship to TableC.
How can I write a SQL query for this?
SELECT
sum(tableA.val) as sumVal,
tableC.data
FROM
tableA
inner join tableB ON tableA.id = tableB.fkeyA
INNER JOIN tableC ON tableB.fkeyC = tableC.id
GROUP by tableC.data
edit
Ah ha - I now see what you're getting at. Let me try again:
SELECT
sum(val) as sumVal,
tableCGroup
FROM
(
SELECT
tableA.val,
(
SELECT cast(tableB.fkeyC as varchar) + ','
FROM tableB WHERE tableB.fKeyA = tableA.id
ORDER BY tableB.fkeyC
FOR XML PATH('')
) as tableCGroup
FROM
tableA
) tmp
GROUP BY
tableCGroup
Hm, in MySQL it could be written like this:
SELECT
SUM(val) AS sumVal
FROM
( SELECT
fkeyA
, GROUP_CONCAT(fkeyC ORDER BY fkeyC) AS grpC
FROM
TableB
GROUP BY
fkeyA
) AS g
JOIN
TableA a
ON a.id = g.fkeyA
GROUP BY
grpC
SELECT sum(a.val)
FROM tablea a
INNER JOIN tableb b ON (b.fKeyA = a.id)
GROUP BY b.fKeyC
It seems that is it needed to create a key_list in orther to allow group by:
75 -> key list = "1 2"
30 -> key list = "1 2 3"
Because GROUP_CONCAT don't exists in T-SQL:
WITH CTE ( Id, key_list )
AS ( SELECT TableA.id, CAST( '' AS VARCHAR(8000) )
FROM TableA
GROUP BY TableA.id
UNION ALL
SELECT TableA.id, CAST( key_list + ' ' + str(TableB.id) AS VARCHAR(8000) )
FROM CTE c
INNER JOIN TableA A
ON c.Id = A.id
INNER join TableB B
ON B.Id = A.id
WHERE A.id > c.id --avoid infinite loop
)
Select
sum( val )
from
TableA inner join
CTE on (tableA.id = CTE.id)
group by
CTE.key_list