SQL Know the most ids in two columns - sql

I want to know what id_b exist the most times in each id_A. I have this table
id_A id_B
1 1
2 1
2 1
3 1
3 3
3 3
And I need a sql command that puts the table like this:
id_A id_B
1 1
2 1
3 3

Technically, this is called the "mode" of (the distribution of) the values.
If you only want one, then use can use the ANSI standard function row_number() with conditional aggregation:
select id_A, id_B as mode_id_B
from (select id_A, id_B, count(*) as cnt,
row_number() over (partition by id_A order by count(*) desc) as seqnum
from t
group by id_A, id_B
) ab
where seqnum = 1;

Related

Oracle SQL: receive ID of grouped foreign key with smallest Date

I have a table given.
I need the ID of each BID with the smallest MODIFIED date
ID
BID
MODIFIED
1
1
01.01.2020
2
1
01.07.2020
3
2
04.08.2020
4
2
04.06.2020
5
2
01.07.2020
6
2
01.10.2020
7
3
01.09.2020
Desired output:
ID
BID
MODIFIED
1
1
01.01.2020
4
2
04.06.2020
7
3
01.09.2020
so far, I can get a list of BIDs with the smallest MODIFIED date, but not the ID from it:
select BID, min(MODIFIED) from MY_TABLE group by BID
how can I receive the ID, however?
Oracle has a "first" aggregation function, which uses the keep syntax:
select BID, min(MODIFIED),
min(id) keep (dense_rank first over order by modified) as id
from MY_TABLE
group by BID;
A common alternative uses window functions:
select t.*
from (select t.*,
row_number() over (partition by bid order by modified asc) as seqnum
from my_table t
) t
where seqnum = 1;

row_number() in T-SQL

I have a table with these situation:
With my code
select
ID_A,
ID_B,
Position,
row_number() over (partition by ID_A, ID_B order by position) as row
from
TB_EXAMPLE
order by
ID_A, Position
I added a row_number_a column, but I want the row_number_b situation. Do you have any hints?
It's a gaps&islands problem, you need to assign a group number to each row before the Row_number. There are several ways, the following is based on the fact that the difference between two sequential number is the same as long as there's no gap in one of the sequences:
position rownum diff
1 1 0
2 1 1
3 1 2
4 1 3
5 2 3
3 3 3
...
22 1 21
23 2 21
24 2 22
Now all rows with the same ID_A and ID_B get the same difference if position is sequential and you can use this value in PARTITION BY:
with cte as
(
select
ID_A
,ID_B
,Position
,position -- if position is not sequential: ROW_NUMBER()over(partition by ID_A order by position)
- ROW_NUMBER()over(partition by ID_A,ID_B order by position) as grp
from TB_EXAMPLE
)
select
ID_A
,ID_B
,Position
,ROW_NUMBER()over(partition by ID_A, ID_B, grp order by position) as rn
from cte
order by ID_A,Position
Based on your comment that you actually want to delete rows, this can be simplified to a check if the previous row's values are the same as the current row's:
with cte as
(
select
ID_A
,ID_B
,Position
,case when LAG(ID_A)over(order by position) = ID_A
and LAG(ID_B)over(order by position) = ID_B
then 'delete'
else 'keep'
end as flag
from TB_EXAMPLE
)
select *
from cte
where flag = 'delete'
And as it looks like this is only based on changes in ID_B:
with cte as
(
select
ID_A
,ID_B
,Position
,case when LAG(ID_B)over(partition by ID_A order by position) = ID_B
then 'delete'
else 'keep'
end as flag
from TB_EXAMPLE
)
select *
from cte
where flag = 'delete'

Query to group based on the sorted table result

Below is my table
a 1
a 2
a 1
b 1
a 2
a 2
b 3
b 2
a 1
My Expected output is
a 4
b 1
a 4
b 5
a 1
I want them to be grouped if they are in sequence.
If your dbms supports window functions, you can use the row_number difference to assign the same group to consecutive values (which are the same) in one column. After assigning the groups, it is easy to sum the values for each group.
select col1,sum(col2)
from (select t.*,
row_number() over(order by someid)
- row_number() over(partition by col1 order by someid) as grp
from tablename t
) x
group by col1,grp
Replace tablename, col1,col2,someid with the appropriate column names. someid should be the column to be ordered by.

Enumerating rows in a inner join

My table
id name num
1 a 3
2 b 4
I need to return every row num number of times. I do it this way.
select DB.BAN_KEY as BAN_KEY, DB.CUST_FULLNAME as CUST_FULLNAME
from TST_DIM_BAN_SELECTED DB
inner join (select rownum rn from dual connect by level < 10) a
on a.rn <= DB.N
There resulting table looks like this.
id name
1 a
1 a
1 a
2 b
2 b
2 b
2 b
But I also need every row in the group to be numbered like this.
id name row_num
1 a 1
1 a 2
1 a 3
2 b 1
2 b 2
2 b 3
2 b 4
How can I do it?
You don't need an inner join to a dummy table or an analytic function to generate the row numbers; you could just use connect by (and its corresponding level function) on the table itself, like so:
WITH tst_dim_ban_selected AS (SELECT 1 ban_key, 'a' cust_fullname, 3 n FROM dual UNION ALL
SELECT 2 ban_key, 'b' cust_fullname, 4 n FROM dual)
-- end of mimicking your table with data in it. See SQL below
SELECT db.ban_key,
db.cust_fullname,
LEVEL row_num
FROM tst_dim_ban_selected db
CONNECT BY LEVEL <= db.n
AND PRIOR db.ban_key = db.ban_key -- assuming this is the primary key
AND PRIOR sys_guid() IS NOT NULL;
BAN_KEY CUST_FULLNAME ROW_NUM
---------- ------------- ----------
1 a 1
1 a 2
1 a 3
2 b 1
2 b 2
2 b 3
2 b 4
If you have other columns than ban_key in the table's primary key, you need to make sure they are included in the connect by clause's list of prior <column> = <column>s. This is so the connect by can identify each row uniquely, meaning that it's looping just over that row and no others. The PRIOR sys_guid() IS NOT NULL is required to prevent connect by loops from occurring.
You can use analytic function for this:
Select id, name,
row_number() over (partition by id, name order by id, name)
From(/* your query */) t;
This can be done without subquery:
Select id, name,
row_number() over (partition by id, name order by id, name)
From /* joins */
You could use this:
SELECT db.ban_key AS ban_key, db.cust_fullname AS cust_fullname,
ROW_NUMBER() OVER (PARTITION BY db.n ORDER BY db.ban_key) AS row_num
FROM tst_dim_ban_selected db
INNER JOIN (SELECT rownum rn FROM dual CONNECT BY level < 10) a
ON a.rn <= db.n;
Use a recursive sub-query factoring clause:
WITH split ( id, name, rn, n ) AS (
SELECT BAN_KEY, CUST_FULLNAME, 1, N
FROM TST_DIM_BAN_SELECTED
UNION ALL
SELECT id, name, rn + 1, n
FROM split
WHERE rn < n
)
SELECT id, name, rn
FROM split;

Select row minimum col for each id

I have a table like following
id id_a id_b uds
--------------------------
1 1 3 20
1 2 8 17
2 1 3 5
3 1 1 32
3 2 1 6
What I would need is to get the row with minimum "uds" for each "id". So the result would be:
id id_a id_b uds
--------------------------
1 2 8 17
2 1 3 5
3 2 1 6
Thank you in advance...
Use Min with a group by clause:
select id, id_a, id_b, min(uds) as uds
from table1
group by id, id_a, id_b
order by id, id_a, id_b;
However, I should mention this is going to get you all of the items, you need to also specify an aggregate on the other columns, or do not include them.
select id, min(uds) as uds
from table1
group by id
order by id;
Judging by your desired output though, the following may be what you want:
select id, max(id_a) as id_a, max(id_b) as id_b, min(uds) as uds
from table1
group by id
order by id;
Most databases support the ANSI standard window functions. An easy way to do what you want:
select t.*
from (select t.*, row_number() over (partition by id order by uds) as seqnum
from t
) t
where seqnum = 1;
you have to set condition with minimum uds value or you have to decides how many numbers of records you want with minimum uds
More one way:
select
a.*
from
#temp a inner join (select id, min(uds) minUds from #temp group by id) b on
a.id = b.id
and a.uds = b.minUds